На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
Страницы: (2) 1 [2]  все  ( Перейти к последнему сообщению )  
> Запись звука (виснет остановка) , 2 почти одинаковые проги, всю сломал голову
    Цитата
    аудиоустройство по умолчанию всегда первое (нулевое)

    Чё-то не то. Через пару дней приеду домой, подключу вторую ззвуковуху, проверю.
    Цитата
    А вызывать каждый раз в callback'е waveInGetDevCaps

    Есть waveingetid. id и сравнивать. ?
      Цитата Prince @
      Чё-то не то. Через пару дней приеду домой, подключу вторую ззвуковуху, проверю.
      Это реально так. Я попереключал и получается, что устройство по умолчанию всегда оказывается первым в списке.

      Добавлено
      И где-то читал на каком-то забугорном форуме, что мол первое устройство всегда по умолчанию. Хотя, лучше, конечно, проверять, т.к. не уверен, что это документировано...
        Win XP.
        Отпишусь всё же о своих результатах, c функциями waveout. Отчасти они повторяют описанные выше.

        DRVM_MAPPER_PREFERRED_GET работает только с параметром wave_mapper, приведённым к hwaveout - возвращает нулевое значение, толку никакого. Как и от waveoutgetid.

        При смене устройства по умолчанию, новое устроство получает индекс 0, но воспроизведение продолжается на открытом ранее устройстве.
        Cвязь между приложением и открытым устройством через его хендл остаётся действительной.

        wave_mapper выбирает устройство с 0-м индексом, и подключает между приложением и устройством прослойку в виде АСМ(audio compression manager), при необходимости. Как отражается смена устройства по умолчанию на работе ACM для уже открытого устройства - не проверял.

        Цитата
        А вызывать каждый раз в callback'е waveInGetDevCaps и сравнивать название с предыдущим — это как-то тупо, ИМХО.

        Наверное.
        Ещё тупой вариант.
        ExpandedWrap disabled
          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
        ExpandedWrap disabled
          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 с этим событием? Пожалуй, что никак. :scratch:

        Звукозапись, при смене устройства по умолчанию, подвисает на секунду и продолжает работу(хотя, может нужно было перезагрузку сделать, во время тестов системе досталось). При отключении устройства - подвисает на несколько секунд и переходит в режим "стоп".
        Так же примерно ведёт себя и Cool Edit.
        Сообщение отредактировано: Prince -
          По сабжу (что касается зависания).
          Короче, когда используется CALLBACK_WINDOW, то всё ок, а виснет при CALLBACK_FUNCTION.
          Вроде косяков в этом коде не должно быть... местами неоптимально и не очень красиво, но это обучающий код типа :D
          Прикреплённый файлПрикреплённый файлWaveMeter.zip (223,07 Кбайт, скачиваний: 68)
            Jin X
            Запустил у себя у меня не зависло.

            Но вот тут точно не правильно.
            ExpandedWrap disabled
               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.
            Сообщение отредактировано: Pavia -
              Цитата Pavia @
              Запустил у себя у меня не зависло.
              Потому что там CALLBACK_WINDOW стоит. Если раскомментить {$DEFINE CB_FUNC} в начале, то зависнет (при смене устройства по умолчанию). У меня, по крайней мере, виснет. Но в целом тут ошибка есть, т.к. waveInAddBuffer нельзя вызывать из callback-функции (именно функции, не обработчика сообщений).

              Цитата Pavia @
              waveInClose - работает не мгновенно
              Кто сказал? Почему не мгновенно? Почему асинхронно?

              Цитата Pavia @
              И есть третья половина беды. Почему-то асинхронность проявляется только на функции, а на оконном методе нет.
              ???

              Добавлено
              Вот это?
              Цитата Pavia @
              Дело в том что эти функции основаны на сообщениях. Сообщение хотя и отправлено драйверу и получено подтверждение о доставке. На самом деле драйвер его ещё не обработал его.

              Как же тогда вызывать эти функции?
              waveInReset, затем waveInClose (потому что сообщения драйверу в любом случае будут поступать в этом порядке), а после получения WIM_CLOSE (прямо в самой оконной функции) делать waveInUnprepareHeader? А в основной функции гонять во время ожидания Application.ProcessMessages (если не при закрытии, то при последующем открытии хотя бы). Так?
                Цитата 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 внутри я бы не рекомендовал делать. Её можно вынести.

                ExpandedWrap disabled
                  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;
                  Цитата Pavia @
                  Вызывать можно, так все делают. Да и по другому вы никак не сделаете. Даже у MS такие примеры есть. Я считаю данный совет ошибкой документации.
                  По-другому - через обработчик оконных сообщений. В любом случае, подвисает. У тебя нет? Именно с CALLBACK_FUNCTION при отключении устройства или смене устройства по умолчанию (при выборе WAVE_MAPPER).

                  Цитата Pavia @
                  callback - бывают двух видов APC и PPC. (асинхронная и параллельная)
                  Если бы там был PPC, то там кроме установки флага ничего делать нельзя.
                  Поэтому майкрософт везде использует APC.
                  Всякий APC callback вызывается из обработчика очереди сообщений.
                  Поэтому ничем не отличается от сообщений.
                  CALLBACK_FUNCTION (я почти уверен) – это PPC как раз-таки. Иначе кто же её вызывает, если не драйвер?
                  В одном месте вообще прочитал, что она может вызываться чуть ли не из обработчика прерывания.
                  Вот я сейчас провёл простой эксперимент. Добавил в конец btnStartClick строки:
                  ExpandedWrap disabled
                      for i := 1 to 10000 do
                        Application.ProcessMessages;
                      Sleep(10000);
                  И когда используется CALLBACK_WINDOW, callback во время Sleep'а не вызывается. А при CALLBACK_FUNCTION вызывается (т.е. после Sleep'а счётчик "Записано буферов" показывает в одном случае 10, а в другом - 100).
                  И даже так: прописываю...
                  ExpandedWrap disabled
                      for i := 1 to 10000 do
                        Application.ProcessMessages;
                      for j := 1 to 50000 do
                        for k := 1 to 1000000 do ;
                  затем ставлю приоритет задачи REALTIME (в диспетчере задач), нажимаю на запись и жду... CALLBACK_FUNCTION срабатывает, запись идёт.

                  Цитата Pavia @
                  Но вот вызывать waveInUnprepareHeader внутри я бы не рекомендовал делать. Её можно вынести.
                  Почему? Это же обработчик сообщения окна. Какие косяки там могут быть?

                  Добавлено
                  И, кстати, по поводу "майкрософт везде использует APC" не соглашусь в том плане, что Enum-функции как раз PPC используют.
                    Вот ещё один эксперимент.
                    Тот же
                    ExpandedWrap disabled
                        for i := 1 to 10000 do
                          Application.ProcessMessages;
                        for j := 1 to 50000 do
                          for k := 1 to 1000000 do ;
                    Тот же REALTIME, только уже кол-во буферов 1 и размер буфера 441 сэмпл (т.е. 1/100 сек).
                    Запускаю. За 14773 мсек записывается 1470 буферов.
                    2-й тест: 1477 буферов за 14991 мсек.
                    3-й тест: 1500 буферов за 15023.
                    Т.е. пропусков фактически нет (нехватка 2 буферов в последнем тесте - это просто за счёт длительности обработки прерывания и callback-функции, я так понимаю).
                      Jin X
                      Хорошо убедили, что параллельно. Тоже запустил.
                      Но тогда всё хуже, чем я думал.

                      Цитата Jin X @
                      Потому что там CALLBACK_WINDOW стоит. Если раскомментить {$DEFINE CB_FUNC} в начале, то зависнет (при смене устройства по умолчанию). У меня, по крайней мере,

                      А по поводу зависаний ну нет их у меня. Надо на другом ноуте попробовать.
                      Сообщение отредактировано: Pavia -
                        Цитата Pavia @
                        Да верно. Но вот вызывать waveInUnprepareHeader внутри я бы не рекомендовал делать. Её можно вынести.
                        Так что по этому поводу? В чём тут проблема?
                          Цитата Jin X @
                          Так что по этому поводу? В чём тут проблема?

                          Не бери в голову, просто я перестраховываюсь.
                            Цитата Pavia @
                            waveInStop, waveInReset
                            Дело в том что эти функции основаны на сообщениях. Сообщение хотя и отправлено драйверу и получено подтверждение о доставке. На самом деле драйвер его ещё не обработал его.
                            Нужно дождаться пока отработает waveInProc. И выставится бит Done.
                            Кстати, можно поподробнее про это: откуда инфа?
                            Я сейчас немного изучил внутренности waveInReset (через отладчик, в частности) и вижу, что callback вызывается внутри waveInReset. Т.е. callback возвращается в waveInReset (вернее, в одну из его подфункций).

                            Добавлено
                            Получается, что городить вот такую штуку нет смысла:
                            Цитата Jin X @
                            waveInReset, затем waveInClose (потому что сообщения драйверу в любом случае будут поступать в этом порядке), а после получения WIM_CLOSE (прямо в самой оконной функции) делать waveInUnprepareHeader? А в основной функции гонять во время ожидания Application.ProcessMessages (если не при закрытии, то при последующем открытии хотя бы). Так?
                              Иван?! :)
                              Цитата Jin X @
                              Кстати, можно поподробнее про это: откуда инфа?
                                Цитата Jin X @
                                Кстати, можно поподробнее про это: откуда инфа?

                                Давно это было не помню уже.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0535 ]   [ 19 queries used ]   [ Generated: 29.03.24, 12:13 GMT ]