Assuming you know how to find out if a line is muted or not, your target is MM_MIXM_LINE_CHANGE message from mmsystem. You'll receive that message if you register your window as a callback window when you open the mixer that you care to track.
To track all of them you need placeholders for their number and handles:
uses
..., Windows, mmsystem;
..
mixerCount: uint;
mixers: array of HMIXER;
In your form constructor, or wherever you prefer, open the mixers and make your window a callback window:
mixerCount := mixerGetNumDevs;
SetLength(mixers, mixerCount);
for i := 0 to mixerCount - 1 do
begin
res := mixerOpen(@mixers[i] {pMixerHandle}, i {MixerId}, Handle {Callback},0 {HInstance}, CALLBACK_WINDOW {OpenFlags});
if (res <> MMSYSERR_NOERROR) then
//your reaction to a failure
end;
Change your window procedure to handle MM_MIXM_LINE_CHANGE:
OrgWndProc: Windows.WNDPROC;
..
OrgWndProc := Windows.WNDPROC(SetWindowLong(Handle, GWL_WNDPROC, PtrInt(@NewWndProc)));
That's not the only way but I prefer it. Your NewWndProc is something like:
function NewWndProc(Ahwnd: HWND; uMsg: UINT; wParam: WParam; lParam: LParam):LRESULT;stdcall;
begin
case uMsg of
MM_MIXM_CONTROL_CHANGE: ;
MM_MIXM_LINE_CHANGE: //Check if mute status changed - here comes your assumed knowledge;
end;
Result := CallWindowProc(OrgWndProc, Ahwnd, uMsg, wParam, lParam);
end;
When you are done, don't forget to clean up:
for i := 0 to mixerCount - 1 do
mixerClose(mixers[i]);
SetLength(mixers, 0);
And maybe return the original window procedure.