В настройках "Звука" винды для каждого (почти) устройства ввода и вывода есть "формат по умолчанию", см. скриншот. Как его прочитать? Пипец, ищем – не можем ничего найти...
Это значение (в формате WAVEFORMATEX) можно получить запросив свойство PKEY_AudioEngine_DeviceFormat. Создается перечислитель MMDeviceEnumerator, затем у него вызывается GetDefaultAudioEndpoint. У полученного IMMDevice запрашивается PropertyStore в котором и находится ключ PKEY_AudioEngine_DeviceFormat.
Спасибо, но я не понимаю, как это всё проделать. Как создать перечислить MMDeviceEnumerator, вызвать у него GetDefaultAudioEndpoint, запросить PropertyStore. И это всё не на C++
Вот так на Delphi я исправил кусок, но дальше что - не знаю... Код (Text): procedure TfmMain.GetInfo; const PKEY_AudioEngine_DeviceFormat_GUID: TGUID = '{F19F064D-082C-4E27-BC73-6882A1BB8E4C}'; PKEY_AudioEngine_DeviceFormat_pid = 0; // DEFINE_PROPERTYKEY(PKEY_AudioEngine_DeviceFormat, 0xf19f064d, 0x82c, 0x4e27, 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, 0); var FMMDevEnum: IMMDeviceEnumerator; FMMDev: IMMDevice; FEndpoint: IPropertyStore; PropVar: ^tag_inner_PROPVARIANT; PropVar2: tag_inner_PROPVARIANT; Key: _tagpropertykey; begin PropVar := nil; CoCreateInstance(CLASS_MMDeviceEnumerator, nil, CLSCTX_ALL, IID_IMMDeviceEnumerator, FMMDevEnum); FMMDevEnum.GetDefaultAudioEndpoint(eRender, eMultimedia, FMMDev); FMMDev.Activate(IID_IPropertyStore, CLSCTX_ALL, PropVar^, Pointer(FEndPoint)); Key.fmtid := PKEY_AudioEngine_DeviceFormat_GUID; Key.pid := PKEY_AudioEngine_DeviceFormat_pid; // ЗДЕСЬ ВОЗНИКАЕТ ИСКЛЮЧЕНИЕ (EAccessViolation): FEndPoint.GetValue(Key, PropVar2); end; Источник: http://www.cyberforum.ru/delphi-multimedia/thread2034210.html Полный код в аттаче...
Если будет ругаться на типа переменной Key в строке FEndPoint.GetValue(Key, PropVar2); (на Delphi 2007 и выше), переставьте MMDeviceAPI в uses в самый конец списка. Ещё строка FMMDev.Activate(IID_IPropertyStore, CLSCTX_ALL, PropVar^, Pointer(FEndPoint)) выдаёт не ноль. Видимо, там косяк какой-то... ну и дальше х/з... p.s. В идеале, конечно, если бы можно было узнавать параметры не только устройства по умолчанию, а любого (причём, полученного через waveOutDevCaps). И устройства ввода тоже, а не только вывода. Но для начала бы с этим разобраться...
Я так понимаю, VB6 ближе к ассемблеру в том плане, что там нет интерфейсов? Если так, тогда лучше на нём. Если есть, тогда лучше на C++. Но если можно на ассемблере (любом), то было бы лучше всего Потому что с него можно на любой язык перевести...
Уф, разобрался!!!! Понял и про eRender/eCapture и про eMultimedia/eCommunications. Только вот что такое eConsole не совсем понятно... В целом вот так: Код (Text): procedure TfmMain.GetInfo; const PKEY_AudioEngine_DeviceFormat_GUID: TGUID = '{F19F064D-082C-4E27-BC73-6882A1BB8E4C}'; PKEY_AudioEngine_DeviceFormat_pid = 0; // DEFINE_PROPERTYKEY(PKEY_AudioEngine_DeviceFormat, 0xf19f064d, 0x82c, 0x4e27, 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, 0); STGM_READ = 0; var FMMDevEnum: IMMDeviceEnumerator; FMMDev: IMMDevice; FEndPoint: IPropertyStore; Prop: tag_inner_PROPVARIANT; Key: _tagpropertykey; Format: TWaveFormatEx; begin CoCreateInstance(CLASS_MMDeviceEnumerator, nil, CLSCTX_ALL, IID_IMMDeviceEnumerator, FMMDevEnum); FMMDevEnum.GetDefaultAudioEndpoint(eRender, eMultimedia, FMMDev); FMMDev.OpenPropertyStore(STGM_READ, FEndPoint); Key.fmtid := PKEY_AudioEngine_DeviceFormat_GUID; Key.pid := PKEY_AudioEngine_DeviceFormat_pid; // FillChar(Prop, SizeOf(Prop), 0); FEndPoint.GetValue(Key, Prop); Move(Prop.__MIDL____MIDL_itf_mmdeviceapi_0003_00810001.blob.pBlobData^, Format, SizeOf(Format)); Edit1.Text := IntToStr(Format.nChannels); Edit2.Text := IntToStr(Format.nSamplesPerSec); Edit3.Text := IntToStr(Format.wBitsPerSample) end; Thetrik, СПАСИБИЩЕ !!!!!!!!!! Теперь бы выяснить как сделать то же самое, но не для устройства по умолчанию, а для любого устройства...
Код (C++): ... if (FAILED(hr = pEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATEMASK_ALL, &pCollection))) { } if (FAILED(hr = pCollection->GetCount(&uDevicesCount))) { } for (int i = 0; i < uDevicesCount; i++) { if (FAILED(hr = pCollection->Item(i, &pDevice))) { } if (FAILED(hr = pDevice->OpenPropertyStore(STGM_READ, &ps))) { } if (FAILED(hr = ps->GetValue(PKEY_AudioEngine_DeviceFormat, &pv))) { } ...
Спасибо, попробую. А как выяснить имя устройства? И будет ли оно совпадать с именем из waveOutGetDevCaps? Ну или хотя бы порядковый номер будет таким же гарантировано? Потому что открывать-то я его всё равно буду через waveOutOpen...
PKEY_Device_FriendlyName. https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd370819(v=vs.85).aspx
Сделал всё это на MASM32 (пока только для устройства по умолчанию), разобрался как работать с COM Возник только такой вопрос: почему Reference Count = 1 после IMMDeviceEnumerator.Release ? И если я выполняю Release повторно, то на ExitProcess возникает исключение? Код (ASM): .686P .MODEL Flat,StdCall OPTION CASEMAP:NONE INCLUDE \MASM32\INCLUDE\WINDOWS.INC INCLUDE \MASM32\INCLUDE\KERNEL32.INC INCLUDE \MASM32\INCLUDE\OLE32.INC INCLUDE \MASM32\INCLUDE\MSVCRT.INC INCLUDELIB \MASM32\LIB\KERNEL32.LIB INCLUDELIB \MASM32\LIB\OLE32.LIB INCLUDELIB \MASM32\LIB\MSVCRT.LIB DEBUG = 1 ; 0 - режим релиза, 1 - режим отладки ;-- ПСЕВДОНИМЫ, МАКРОСЫ, СТРУКТУРЫ, ИНТЕРФЕЙСЫ ------------------------------------------------------------------------- ; Псевдонимы функций printf EQU crt_printf wprintf EQU crt_wprintf ; Константы eRender = 0 eCapture = 1 eConsole = 0 eMultimedia = 1 eCommunications = 2 STGM_READ = 0 STGM_WRITE = 1 STGM_READWRITE = 2 WAVE_FORMAT_EXTENSIBLE = 0FFFEh ; Определить константу N1&N2&N3 со значением Value Def_CatStr MACRO Value, N1, N2, N3 N1&N2&N3 = Value ENDM ; Вызов функции (по смещению) Ofs объекта Obj с параметрами params cominvk MACRO Obj, Ofs, Params:VARARG LOCAL param, reversed reversed TEXTEQU <> % FOR param,<Params> reversed CATSTR <param>,<!,>,reversed ENDM % FOR param,<reversed> push param ENDM mov eax,Obj push eax mov eax,[eax] call DWORD PTR [eax+Ofs] ENDM ; Определить интерфейс с именем ComName и функциями из списка FuncList Interface MACRO ComName, FuncList:VARARG LOCAL N, func ComName&_QueryInterface = 0*4 ComName&_AddRef = 1*4 ComName&_Release = 2*4 N = 3 FOR func,<FuncList> Def_CatStr N*4, ComName,_,func N = N + 1 ENDM ENDM ; Выполнить Inst, если имя DEBUG определено и не равно 0 DEBUG_ MACRO Inst:VARARG ifdef DEBUG if DEBUG Inst endif endif ENDM ; Определение функций интерфейсов Interface IMMDeviceEnumerator, EnumAudioEndpoints, GetDefaultAudioEndpoint, GetDevice, RegisterEndpointNotificationCallback, UnregisterEndpointNotificationCallback Interface IMMDevice, Activate, OpenPropertyStore, GetId, GetState Interface IPropertyStore, GetCount, GetAt, GetValue, SetValue, Commit BLOB STRUCT cbSize DD ? pBlobData DD ? BLOB ENDS PropVariant STRUCT vt DW ? wRes1 DB ? wRes2 DB ? wRes3 DD ? blob BLOB <> PropVariant ENDS ;-- ДАННЫЕ ------------------------------------------------------------------------------------------------------------- .DATA fmtInfo DB 'Default Wave Format:',13,10 DB ' FormatTag = %i%s',13,10 DB ' SamplesPerSec = %i',13,10 DB ' BitsPerSample = %i',13,10 DB ' Channels = %i',13,10 DB ' AvgBytesPerSec = %i',13,10 DB ' BlockAlign = %i',13,10,0 fmtExtraInfo DB 10,'Extra Info:',13,10 DB ' ValidBitsPerSample = %i',13,10 DB ' ChannelMask = %08X (hex)',13,10 DB ' SubFormat GUID = ',0 strWaveFmtExt DB ' (WAVE_FORMAT_EXTENSIBLE)' strZero DB 0 strError DB 'Interface error :(' strNewLine DB 13,10,0 DEBUG_ fmtDebug DB '%s = %i',13,10,0 DEBUG_ strCoCreateInstance DB 'DEBUG: CoCreateInstance(IMMDeviceEnumerator)',0 DEBUG_ strIMMDeviceEnumerator DB 'DEBUG: IMMDeviceEnumerator.GetDefaultAudioEndpoint',0 DEBUG_ strIMMDevice DB 'DEBUG: IMMDevice.OpenPropertyStore',0 DEBUG_ strIPropertyStore DB 'DEBUG: IPropertyStore.GetValue(PKEY_AudioEngine_DeviceFormat)',0 DEBUG_ strIMMDeviceEnumeratorRelease DB 'DEBUG: IMMDeviceEnumerator.Release',0 DEBUG_ strIMMDeviceRelease DB 'DEBUG: IMMDevice.Release',0 DEBUG_ strIPropertyStoreRelease DB 'DEBUG: IPropertyStore.Release',0 CLASS_MMDeviceEnumerator GUID {0BCDE0395h,0E52Fh,0467Ch,{08Eh,03Dh,0C4h,057h,092h,091h,069h,02Eh}} IID_IMMDeviceEnumerator GUID {0A95664D2h,09614h,04F35h,{0A7h,046h,0DEh,08Dh,0B6h,036h,017h,0E6h}} PKEY_AudioEngine_DeviceFormat GUID {0F19F064Dh,0082Ch,04E27h,{0BCh,073h,068h,082h,0A1h,0BBh,08Eh,04Ch}} DD 0 ; часть PKEY_AudioEngine_DeviceFormat MMDevEnum DD 0 MMDev DD 0 PropStore DD 0 .DATA? Prop PropVariant <> GUIDString DW 40 dup (?) InputRec INPUT_RECORD <> ReallyRead DD ? ;-- ОСНОВНОЙ КОД ------------------------------------------------------------------------------------------------------- .CODE Start: invoke CoInitialize, NULL ; CoCreateInstance(CLASS_MMDeviceEnumerator, nil, CLSCTX_ALL, IID_IMMDeviceEnumerator, MMDevEnum) invoke CoCreateInstance, ADDR CLASS_MMDeviceEnumerator, NULL, CLSCTX_ALL, ADDR IID_IMMDeviceEnumerator, ADDR MMDevEnum DEBUG_ push eax DEBUG_ invoke printf, ADDR fmtDebug, ADDR strCoCreateInstance, eax DEBUG_ pop eax test eax,eax jnz Error ; MMDevEnum.GetDefaultAudioEndpoint(eRender, eMultimedia, MMDev) cominvk MMDevEnum, IMMDeviceEnumerator_GetDefaultAudioEndpoint, eRender, eMultimedia, OFFSET MMDev DEBUG_ push eax DEBUG_ invoke printf, ADDR fmtDebug, ADDR strIMMDeviceEnumerator, eax DEBUG_ pop eax test eax,eax jnz Error ; MMDev.OpenPropertyStore(STGM_READ, PropStore) cominvk MMDev, IMMDevice_OpenPropertyStore, STGM_READ, OFFSET PropStore DEBUG_ push eax DEBUG_ invoke printf, ADDR fmtDebug, ADDR strIMMDevice, eax DEBUG_ pop eax test eax,eax jnz Error ; PropStore.GetValue(Key, Prop) cominvk PropStore, IPropertyStore_GetValue, OFFSET PKEY_AudioEngine_DeviceFormat, OFFSET Prop DEBUG_ push eax DEBUG_ invoke printf, ADDR fmtDebug, ADDR strIPropertyStore, eax DEBUG_ pop eax test eax,eax jnz Error DEBUG_ invoke printf, ADDR strNewLine ; Выводим результаты mov ebx,Prop.blob.pBlobData movzx esi,[ebx+WAVEFORMATEX.wFormatTag] movzx eax,[ebx+WAVEFORMATEX.wBitsPerSample] movzx ecx,[ebx+WAVEFORMATEX.nChannels] movzx edx,[ebx+WAVEFORMATEX.nBlockAlign] mov edi,OFFSET strZero cmp esi,WAVE_FORMAT_EXTENSIBLE jne NotExt mov edi,OFFSET strWaveFmtExt NotExt: invoke printf, ADDR fmtInfo, esi, edi, [ebx+WAVEFORMATEX.nSamplesPerSec], eax, ecx, [ebx+WAVEFORMATEX.nAvgBytesPerSec], edx ; Дополнительные данные cmp esi,WAVE_FORMAT_EXTENSIBLE jne Finish movzx eax,WORD PTR [ebx+18] invoke printf, ADDR fmtExtraInfo, eax, DWORD PTR [ebx+20] lea eax,[ebx+24] invoke StringFromGUID2, eax, ADDR GUIDString, LENGTHOF GUIDString ; переводим GUID в Unicode-строку invoke wprintf, ADDR GUIDString invoke printf, ADDR strNewLine Finish: ; Освобождаем все интерфейсы DEBUG_ invoke printf, ADDR strNewLine cmp MMDevEnum,0 je @@NoMMDevEnum @@: cominvk MMDevEnum, IMMDeviceEnumerator_Release ;DEBUG_ push eax DEBUG_ invoke printf, ADDR fmtDebug, ADDR strIMMDeviceEnumeratorRelease, eax ;DEBUG_ pop eax ; test eax,eax ; jnz @B @@NoMMDevEnum: cmp MMDev,0 je @@NoMMDev @@: cominvk MMDev, IMMDevice_Release ;DEBUG_ push eax DEBUG_ invoke printf, ADDR fmtDebug, ADDR strIMMDeviceRelease, eax ;DEBUG_ pop eax ; test eax,eax ; jnz @B @@NoMMDev: cmp PropStore,0 je @@NoPropStore @@: cominvk PropStore, IPropertyStore_Release ;DEBUG_ push eax DEBUG_ invoke printf, ADDR fmtDebug, ADDR strIPropertyStoreRelease, eax ;DEBUG_ pop eax ; test eax,eax ; jnz @B @@NoPropStore: ; Ожидаем нажатия любой клавиши invoke GetStdHandle, STD_INPUT_HANDLE mov ebx,eax @@LoopInput: invoke ReadConsoleInput, ebx, offset InputRec, 1, offset ReallyRead test eax,eax jz @@Error cmp InputRec.EventType, KEY_EVENT jne @@LoopInput cmp InputRec.KeyEvent.bKeyDown, 0 je @@LoopInput @@Error: invoke ExitProcess, NULL Error: invoke printf, ADDR strError ; сообщение об ошибке jmp Finish END Start
Ты не должен брать во внимание это значение, нужно просто вызывать столько раз Release сколько было вызвано AddRef + 1. Внутренняя реализация объекта MMDeviceEnumerator из CoCreateInstance уже возвращает объект со значением счетчика 2, а сам объект уничтожается только из DllMain. Однако, когда ты вызываешь Release дважды то в обработчике DLL_PROCESS_DETTACH код обращается к объекту которого уже нет и происходит исключение.
Ну вообще была такая мысль, только сделаю получение списка устройств (руки должны дойти) . Хотя обещать лучше не буду Вопрос ещё такой: как связать имя устройства через IMM-интерфейсы с именем waveIn/OutDevCaps? Там, как я понимаю, разные будут названия... хотя, пока не пробовал p.s. А по-хорошему бы надо разобраться ещё с оповещениями и изменением громкости, но это можно уже потом описать, если что...