Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[54.175.120.161] |
|
Страницы: (2) 1 [2] все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
Цитата аудиоустройство по умолчанию всегда первое (нулевое) Чё-то не то. Через пару дней приеду домой, подключу вторую ззвуковуху, проверю. Цитата А вызывать каждый раз в callback'е waveInGetDevCaps Есть waveingetid. id и сравнивать. ? |
Сообщ.
#17
,
|
|
|
Цитата Prince @ Это реально так. Я попереключал и получается, что устройство по умолчанию всегда оказывается первым в списке. Чё-то не то. Через пару дней приеду домой, подключу вторую ззвуковуху, проверю. Добавлено И где-то читал на каком-то забугорном форуме, что мол первое устройство всегда по умолчанию. Хотя, лучше, конечно, проверять, т.к. не уверен, что это документировано... |
Сообщ.
#18
,
|
|
|
Win XP.
Отпишусь всё же о своих результатах, c функциями waveout. Отчасти они повторяют описанные выше. DRVM_MAPPER_PREFERRED_GET работает только с параметром wave_mapper, приведённым к hwaveout - возвращает нулевое значение, толку никакого. Как и от waveoutgetid. При смене устройства по умолчанию, новое устроство получает индекс 0, но воспроизведение продолжается на открытом ранее устройстве. Cвязь между приложением и открытым устройством через его хендл остаётся действительной. wave_mapper выбирает устройство с 0-м индексом, и подключает между приложением и устройством прослойку в виде АСМ(audio compression manager), при необходимости. Как отражается смена устройства по умолчанию на работе ACM для уже открытого устройства - не проверял. Цитата А вызывать каждый раз в callback'е waveInGetDevCaps и сравнивать название с предыдущим — это как-то тупо, ИМХО. Наверное. Ещё тупой вариант. const DRV_QUERYDEVICEINTERFACESIZE = $0800 + 13; DRV_QUERYDEVICEINTERFACE = $0800 + 12; var dw1,dw2 :dword; pc :pwidechar; waveoutmessage(0,DRV_QUERYDEVICEINTERFACESIZE,dword(@dw1),dword(@dw2)); waveoutmessage(0,DRV_QUERYDEVICEINTERFACE,dword(pc),dw1); Возвращаемая в pc строка "более уникальная", по сравнению с zsname из waveoutdevcaps. Например, для встроенной карты: '\\?\hdaudio#func_01&ven_10ec&dev_0883&subsys_1462034a&rev_1000#4&3aca8a72&0&0001#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\rearlineoutwave3' Попытка подписаться на сообщения DEVCLASS_WAVEOUT при помощи RegisterDeviceNotification ни к чему не привела. https://msdn.microsoft.com/en-us/library/ee...bedded.60).aspx const DEVCLASS_WAVEOUT_GUID:TGUID = '{25D535D0-5D7A-4a35-9741-ECD0B09FDB46}'; type TDEV_BROADCAST_DEVICEINTERFACE = record dbcc_size: DWORD; dbcc_devicetype: DWORD; dbcc_reserved: DWORD; dbcc_classguid: TGUID; dbcc_name: short; end; var Info:TDEV_BROADCAST_DEVICEINTERFACE; // В FormCreate: FWndHandle:=AllocateHWnd(WndProc); Info.dbcc_size:=SizeOf(TDEV_BROADCAST_DEVICEINTERFACE); Info.dbcc_devicetype:=DBT_DEVTYP_DEVICEINTERFACE; Info.dbcc_classguid:=DEVCLASS_WAVEOUT_GUID; NotifyH:=RegisterDeviceNotification(FWndHandle,@Info,DEVICE_NOTIFY_WINDOW_HANDLE); // В FormClose UnregisterDeviceNotification(NotifyH); DeallocateHWnd(FWndHandle); В оконную процедуру ничего не прилетает. C другой стороны, при смене устройства по умолчанию, сыпятся несколько(3 штуки) сообщений 49310 с параметрами wparam=0 lparam=0. Это не системное сообщение, что оно означает и почему несколько сообщений... Поскольку смена устройства по умолчанию сама по себе, без отключения устройства, по большому счёту, не сказывается на работе связки "приложение - ранее открытое устройство", возможно использовать и тупые варианты для проверки смены устройства по умолчанию. При отключении/подключении устройства приходят сообщения WM_DEVICECHANGE, но они, во-первых, не связаны конкретно с аудиоустройствами(на аудиоустройства, как я написал выше, подписаться не удалось) и не позволяют идентифицировать устройство (wparam=7 lparam=0). Во-вторых, даже если отловить манипуляции с устройством посредством WM_DEVICECHANGE(например, на USB устройства подписаться можно), как надёжно синхронизировать работу waveaudio с этим событием? Пожалуй, что никак. Звукозапись, при смене устройства по умолчанию, подвисает на секунду и продолжает работу(хотя, может нужно было перезагрузку сделать, во время тестов системе досталось). При отключении устройства - подвисает на несколько секунд и переходит в режим "стоп". Так же примерно ведёт себя и Cool Edit. |
Сообщ.
#19
,
|
|
|
По сабжу (что касается зависания).
Короче, когда используется CALLBACK_WINDOW, то всё ок, а виснет при CALLBACK_FUNCTION. Вроде косяков в этом коде не должно быть... местами неоптимально и не очень красиво, но это обучающий код типа Прикреплённый файлWaveMeter.zip (223,07 Кбайт, скачиваний: 68) |
Сообщ.
#20
,
|
|
|
Jin X
Запустил у себя у меня не зависло. Но вот тут точно не правильно. Stop := True; if Reset then waveInReset(WaveH); Err := waveInClose(WaveH); if Err <> MMSYSERR_NOERROR then begin waveInGetErrorText(Err, @ErrMsg, SizeOf(ErrMsg)); MessageBox(0, ErrMsg, PChar('Ошибка '+IntToStr(Err)), MB_OK or MB_ICONERROR or MB_TASKMODAL); end; // Тут не хватает WaitDone for i := 0 to BufN-1 do waveInUnprepareHeader(WaveH, @WaveHdr[i], SizeOf(WaveHdr[i])); waveInClose - работает не мгновенно. А через вызов вашей функции. Но это полбеды.Вторая половина делает он это асинхронно то есть без блокировки вашего кода. Поэтому ваш код не дожидаясь освобождения буферов освобождает общую память (функция waveInUnprepareHeader). Что приводит к невалидным указателям. И есть третья половина беды. Почему-то асинхронность проявляется только на функции, а на оконном методе нет. waveInClose - сначала освобождает все буферы, WIM_DATA и только после WIM_CLOSE. |
Сообщ.
#21
,
|
|
|
Цитата Pavia @ Потому что там CALLBACK_WINDOW стоит. Если раскомментить {$DEFINE CB_FUNC} в начале, то зависнет (при смене устройства по умолчанию). У меня, по крайней мере, виснет. Но в целом тут ошибка есть, т.к. waveInAddBuffer нельзя вызывать из callback-функции (именно функции, не обработчика сообщений).Запустил у себя у меня не зависло. Цитата Pavia @ Кто сказал? Почему не мгновенно? Почему асинхронно?waveInClose - работает не мгновенно Цитата Pavia @ ??? И есть третья половина беды. Почему-то асинхронность проявляется только на функции, а на оконном методе нет. Добавлено Вот это? Цитата Pavia @ Дело в том что эти функции основаны на сообщениях. Сообщение хотя и отправлено драйверу и получено подтверждение о доставке. На самом деле драйвер его ещё не обработал его. Как же тогда вызывать эти функции? waveInReset, затем waveInClose (потому что сообщения драйверу в любом случае будут поступать в этом порядке), а после получения WIM_CLOSE (прямо в самой оконной функции) делать waveInUnprepareHeader? А в основной функции гонять во время ожидания Application.ProcessMessages (если не при закрытии, то при последующем открытии хотя бы). Так? |
Сообщ.
#22
,
|
|
|
Цитата Jin X @ Но в целом тут ошибка есть, т.к. waveInAddBuffer нельзя вызывать из callback-функции (именно функции, не обработчика сообщений). Вызывать можно, так все делают. Да и по другому вы никак не сделаете. Даже у MS такие примеры есть. Я считаю данный совет ошибкой документации. callback - бывают двух видов APC и PPC. (асинхронная и параллельная) Если бы там был PPC, то там кроме установки флага ничего делать нельзя. Поэтому майкрософт везде использует APC. Всякий APC callback вызывается из обработчика очереди сообщений. Поэтому ничем не отличается от сообщений. Но на практике отличия всё же есть. Так как функции вида wait* основаны на APC. То если мы заблокируем очередь сообщений то они не будут работать. - попросту всё зависнет. Поэтому wait* запрещено использовать в callback и обработчиках сообщений. Цитата Jin X @ Как же тогда вызывать эти функции? Цитата Jin X @ Как же тогда вызывать эти функции? waveInReset, затем waveInClose (потому что сообщения драйверу в любом случае будут поступать в этом порядке), а после получения WIM_CLOSE (прямо в самой оконной функции) делать waveInUnprepareHeader? А в основной функции гонять во время ожидания Application.ProcessMessages? Так? Да верно. Но вот вызывать waveInUnprepareHeader внутри я бы не рекомендовал делать. Её можно вынести. procedure WaitClosed(timeout:Integer); var T0,elapsed:Integer; begin T0:=GettickCount(); repeat Application.ProcessMessages; elapsed:=GettickCount()-T0; until (elapsed>timeout) or EventClosed=True; end; ... EventClosed:=False; Stop := True; if Reset then waveInReset(WaveH); Err := waveInClose(WaveH); if Err <> MMSYSERR_NOERROR then begin waveInGetErrorText(Err, @ErrMsg, SizeOf(ErrMsg)); MessageBox(0, ErrMsg, PChar('Ошибка '+IntToStr(Err)), MB_OK or MB_ICONERROR or MB_TASKMODAL); end; WaitClosed(4000); for i := 0 to BufN-1 do waveInUnprepareHeader(WaveH, @WaveHdr[i], SizeOf(WaveHdr[i])); .... procedure waveInProc(hwi: HWAVEIN; uMsg, dwInstance, dwParam1, dwParam2: DWord); stdcall; var i, N, wMin, wMax: Integer; begin if uMsg = WIM_CLOSE then EventClosed:=True; // тут лучше счётчик свободных буферов добавить и по нему проверять. if uMsg = WIM_DATA then begin Inc(Bufs); with PWAVEHDR(dwParam1)^ do begin WaveNow := 0; wMin := 0; wMax := 0; for i := 0 to dwBytesRecorded div 2-1 do begin N := PBuf(lpData)^[i]; if N < wMin then wMin := N; if N > wMax then wMax := N end; N := (wMax - wMin) div 2; WaveNow := N; if N > WaveMax then WaveMax := N; if not Stop then begin if WaveHdr[dwUser].dwFlags and WHDR_DONE <> 0 then Done := True; dwFlags := dwFlags and (not WHDR_DONE); dwBytesRecorded := 0; dwBufferLength := SizeOf(Buf[0]) div SD; waveInAddBuffer(WaveH, PWAVEHDR(dwParam1), SizeOf(WaveHdr[0])); CurBuf := (CurBuf+1) mod BufN end end end end; |
Сообщ.
#23
,
|
|
|
Цитата Pavia @ По-другому - через обработчик оконных сообщений. В любом случае, подвисает. У тебя нет? Именно с CALLBACK_FUNCTION при отключении устройства или смене устройства по умолчанию (при выборе WAVE_MAPPER).Вызывать можно, так все делают. Да и по другому вы никак не сделаете. Даже у MS такие примеры есть. Я считаю данный совет ошибкой документации. Цитата Pavia @ CALLBACK_FUNCTION (я почти уверен) – это PPC как раз-таки. Иначе кто же её вызывает, если не драйвер?callback - бывают двух видов APC и PPC. (асинхронная и параллельная) Если бы там был PPC, то там кроме установки флага ничего делать нельзя. Поэтому майкрософт везде использует APC. Всякий APC callback вызывается из обработчика очереди сообщений. Поэтому ничем не отличается от сообщений. В одном месте вообще прочитал, что она может вызываться чуть ли не из обработчика прерывания. Вот я сейчас провёл простой эксперимент. Добавил в конец btnStartClick строки: for i := 1 to 10000 do Application.ProcessMessages; Sleep(10000); И даже так: прописываю... for i := 1 to 10000 do Application.ProcessMessages; for j := 1 to 50000 do for k := 1 to 1000000 do ; Цитата Pavia @ Почему? Это же обработчик сообщения окна. Какие косяки там могут быть? Но вот вызывать waveInUnprepareHeader внутри я бы не рекомендовал делать. Её можно вынести. Добавлено И, кстати, по поводу "майкрософт везде использует APC" не соглашусь в том плане, что Enum-функции как раз PPC используют. |
Сообщ.
#24
,
|
|
|
Вот ещё один эксперимент.
Тот же for i := 1 to 10000 do Application.ProcessMessages; for j := 1 to 50000 do for k := 1 to 1000000 do ; Запускаю. За 14773 мсек записывается 1470 буферов. 2-й тест: 1477 буферов за 14991 мсек. 3-й тест: 1500 буферов за 15023. Т.е. пропусков фактически нет (нехватка 2 буферов в последнем тесте - это просто за счёт длительности обработки прерывания и callback-функции, я так понимаю). |
Сообщ.
#25
,
|
|
|
Jin X
Хорошо убедили, что параллельно. Тоже запустил. Но тогда всё хуже, чем я думал. Цитата Jin X @ Потому что там CALLBACK_WINDOW стоит. Если раскомментить {$DEFINE CB_FUNC} в начале, то зависнет (при смене устройства по умолчанию). У меня, по крайней мере, А по поводу зависаний ну нет их у меня. Надо на другом ноуте попробовать. |
Сообщ.
#26
,
|
|
|
Цитата Pavia @ Так что по этому поводу? В чём тут проблема? Да верно. Но вот вызывать waveInUnprepareHeader внутри я бы не рекомендовал делать. Её можно вынести. |
Сообщ.
#27
,
|
|
|
Цитата Jin X @ Так что по этому поводу? В чём тут проблема? Не бери в голову, просто я перестраховываюсь. |
Сообщ.
#28
,
|
|
|
Цитата Pavia @ Кстати, можно поподробнее про это: откуда инфа?waveInStop, waveInReset Дело в том что эти функции основаны на сообщениях. Сообщение хотя и отправлено драйверу и получено подтверждение о доставке. На самом деле драйвер его ещё не обработал его. Нужно дождаться пока отработает waveInProc. И выставится бит Done. Я сейчас немного изучил внутренности waveInReset (через отладчик, в частности) и вижу, что callback вызывается внутри waveInReset. Т.е. callback возвращается в waveInReset (вернее, в одну из его подфункций). Добавлено Получается, что городить вот такую штуку нет смысла: Цитата Jin X @ waveInReset, затем waveInClose (потому что сообщения драйверу в любом случае будут поступать в этом порядке), а после получения WIM_CLOSE (прямо в самой оконной функции) делать waveInUnprepareHeader? А в основной функции гонять во время ожидания Application.ProcessMessages (если не при закрытии, то при последующем открытии хотя бы). Так? |
Сообщ.
#29
,
|
|
|
Иван?!
Цитата Jin X @ Кстати, можно поподробнее про это: откуда инфа? |
Сообщ.
#30
,
|
|
|
Цитата Jin X @ Кстати, можно поподробнее про это: откуда инфа? Давно это было не помню уже. |