Windows Core Audio API(Win7 64ビットDelphi XE5)のイベントを実装しようとしています。私の目的は、ボリュームミキサーのアプリケーションを追跡して、自分のリストにないオーディオセッションをミュートし、ターゲットアプリケーションの音量を調整することです。オーディオデバイスとセッションを正常に列挙し、オーディオをミュートし、セッションごとにボリュームを調整しますが、私はイベントに苦労しています。私が必要とするのは、新しいセッションが追加されたときやセッションが閉じられたときに、再び列挙できるように通知することです。私はセッションを列挙するためにタイマーを使うことができましたが、私はそれを避けることを好むでしょう。Core Audio APIイベントの実装
動作しない特定のイベントは、IAudioSessionNotification
とIMMNotificationClient
です。
私の質問がされては、次のとおりです。
- が単純すぎるイベント用のクラスを派生する私のアプローチですか?
IAudioEndpointVolumeCallback
は私がUI要素を参照していますので、コード が臭いと思う「作業」されているが - (個人的にテストしていません) Catch audio sessions events が、どちらか動作していないよう:私 はここにはるかに複雑である例を見つけましたOnNotify関数 で私はいくつかのフィードバック/ポインタがほしいと思います。それは有効な実装ですか?
メインフォームを含むuAudioUIと、Core Audioインターフェイスを含むMMDevApiユニットの2つのユニットがあります。簡単にするため
uAudioUI.pas
...
type
TEndpointVolumeCallback = class(TInterfacedObject, IAudioEndpointVolumeCallback)
public
function OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT; stdcall;
end;
TMMNotificationClient = class(TInterfacedObject, IMMNotificationClient)
function OnDefaultDeviceChanged(const flow: EDataFlow; const role: ERole; const pwstrDefaultDevice: LPCWSTR):HRESULT; stdcall;
function OnDeviceAdded(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceRemoved(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceStateChanged(const pwstrDeviceID:LPCWSTR; const dwNewState: DWORD):HRESULT; stdcall;
function OnPropertyValueChanged(const pwstrDeviceID:LPCWSTR; const key: PROPERTYKEY):HRESULT; stdcall;
end;
TAudioMixerSessionCallback = class(TInterfacedObject, IAudioSessionEvents)
function OnDisplayNameChanged(NewDisplayName:LPCWSTR; EventContext:pGuid):HResult; stdcall;
function OnIconPathChanged(NewIconPath:LPCWSTR; EventContext:pGuid):HResult; stdcall;
function OnSimpleVolumeChanged(NewVolume:Single; NewMute:LongBool; EventContext:pGuid):HResult; stdcall;
function OnChannelVolumeChanged(ChannelCount:uint; NewChannelArray:PSingle; ChangedChannel:uint;
EventContext:pGuid):HResult; stdcall;
function OnGroupingParamChanged(NewGroupingParam, EventContext:pGuid):HResult; stdcall;
function OnStateChanged(NewState:uint):HResult; stdcall; // AudioSessionState
function OnSessionDisconnected(DisconnectReason:uint):HResult; stdcall; // AudioSessionDisconnectReason
end;
TAudioSessionCallback = class(TInterfacedObject, IAudioSessionNotification)
function OnSessionCreated(const NewSession: IAudioSessionControl): HResult; stdcall;
end;
:私は必要なインタフェースのためのクラスを派生メインフォーム部において
MMDevApi.pas
...
IAudioEndpointVolumeCallback = interface(IUnknown)
['{657804FA-D6AD-4496-8A60-352752AF4F89}']
function OnNotify(pNotify:PAUDIO_VOLUME_NOTIFICATION_DATA):HRESULT; stdcall;
end;
PIMMNotificationClient = ^IMMNotificationClient;
IMMNotificationClient = interface(IUnknown)
['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
function OnDefaultDeviceChanged(const flow: EDataFlow; const role: ERole; const pwstrDefaultDevice: LPCWSTR):HRESULT; stdcall;
function OnDeviceAdded(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceRemoved(const pwstrDeviceId: LPCWSTR):HRESULT; stdcall;
function OnDeviceStateChanged(const pwstrDeviceID:LPCWSTR; const dwNewState: DWORD):HRESULT; stdcall;
function OnPropertyValueChanged(const pwstrDeviceID:LPCWSTR; const key: PROPERTYKEY):HRESULT; stdcall;
end;
IAudioSessionNotification = interface(IUnknown)
['{641DD20B-4D41-49CC-ABA3-174B9477BB08}']
function OnSessionCreated(const NewSession: IAudioSessionControl): HResult; stdcall;
end;
:
私のコードの現在の関連する部分は、この(そのテストアプリケーション)のように見えます私はグローバル
private
{ Private declarations }
FDefaultDevice : IMMDevice;
FAudioEndpointVolume : IAudioEndpointVolume;
FDeviceEnumerator : IMMDeviceEnumerator;
FAudioClient : IAudioClient;
FAudioSessionManager : IAudioSessionManager2;
FAudioSessionControl : IAudioSessionControl2;
FEndpointVolumeCallback : IAudioEndpointVolumeCallback;
FAudioSessionEvents : IAudioSessionEvents;
FMMNotificationCallback : IMMNotificationClient;
FPMMNotificationCallback : PIMMNotificationClient;
FAudioSessionCallback : TAudioSessionCallback;
...
を使用procedure TForm1.FormCreate(Sender: TObject);
var
...
begin
hr := CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, FDeviceEnumerator);
if hr = ERROR_SUCCESS then
begin
hr := FDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, FDefaultDevice);
if hr <> ERROR_SUCCESS then Exit;
//get the master audio endpoint
hr := FDefaultDevice.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, IUnknown(FAudioEndpointVolume));
if hr <> ERROR_SUCCESS then Exit;
hr := FDefaultDevice.Activate(IID_IAudioClient, CLSCTX_ALL, nil, IUnknown(FAudioClient));
if hr <> ERROR_SUCCESS then Exit;
//volume handler
FEndpointVolumeCallback := TEndpointVolumeCallback.Create;
if FAudioEndpointVolume.RegisterControlChangeNotify(FEndPointVolumeCallback) = ERROR_SUCCESS then
FEndpointVolumeCallback._AddRef;
//device change/ex: cable unplug handler
FMMNotificationCallback := TMMNotificationClient.Create;
FPMMNotificationCallback := @FMMNotificationCallback;
if FDeviceEnumerator.RegisterEndpointNotificationCallback(FPCableUnpluggedCallback) = ERROR_SUCCESS then
FMMNotificationCallback._AddRef;
...そして最後に、クラス関数
{ TEndpointVolumeCallback }
function TEndpointVolumeCallback.OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
var
audioLevel : integer;
begin
//NOTE: this works..
audioLevel := Round(pNotify.fMasterVolume * 100);
Form1.trackVolumeLevel.Position := audioLevel;
if pNotify.bMuted then
begin
form1.trackVolumeLevel.Enabled := False;
form1.spdMute.Caption := 'X';
end
else
begin
form1.trackVolumeLevel.Enabled := True;
form1.spdMute.Caption := 'O';
end;
Result := S_OK;
end;
{ TMMNotificaionClient }
function TMMNotificationClient.OnDefaultDeviceChanged(const flow: EDataFlow; const role: ERole; const pwstrDefaultDevice: LPCWSTR): HRESULT;
begin
//NOTE: this crashes - referencing a pointer to add 000000000
Form1.Label2.Caption := 'Audio device changed';
Result := S_OK;
end;
{ AudioMixerSessionCallback }
function TAudioMixerSessionCallback.OnSimpleVolumeChanged(NewVolume: Single; NewMute: LongBool; EventContext: PGUID): HRESULT;
begin
//NOTE: This works...
Form1.trackSessionVolumeLevel.Position := Round(NewVolume * 100);
Form1.Label2.Caption := EventContext.ToString;
Result := S_OK;
end;
{ AudioSessionCallback }
function TAudioSessionCallback.OnSessionCreated(const NewSession: IAudioSessionControl): HRESULT;
begin
//NOTE: This never gets called...
Form1.Label2.Caption := 'New audio session created';
Result := S_OK;
end;
質問2:イベントはメインスレッドで発生した場合、あなたは同期させる必要がありますされていない場合(、()GetCurrentThreadIdに確認してください)。 – whosrdaddy
@whordaddy、ありがとう。 GetCurrentThreadId()は、偶数がメインスレッドで実行されていないことを示します。私が読んだことから、synchronize()は設計上悪いと思われ、PostMessage/SendMessageは良いでしょう。 – lowrider
Quesion 1に関して、私はこのようなクラスを実装しました。http://stackoverflow.com/questions/858974/iaudiosessionnotification-anyone-have-working-codeそして同じ効果。イベントは呼び出されません。このエクササイズが私に理解させたのは、PostMessageを使ったそのようなクラスの価値です。イベントデータをクラスに格納し、PostMessageを呼び出して、メインスレッドのクラスからデータを取得できます。 – lowrider