На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела "Программирование звука"
0) Данный раздел предназначен для обсуждения проблем, возникающих при программировании задач, связанных с записью, обработкой, воспроизведением звука. Перед созданием темы подумайте, не будет ли она уместнее в разделах Разработка и тестирование программ, Наши исходники, а особенно Разовые заказы и подработки
1) На Раздел распространяются все Правила форума.Огромная просьба с ними внимательно ознакомиться.
2) Запрещается давать бессмысленные ответы вроде: "Снеси Мастдай", "ХП рулит", "Поставь Линукс" и т.д.
3) Запрещается создавать темы, в которых Вы намереваетесь получить ссылку на кряки, серийники и т.п. Также запрещено любое обсуждение p2p (peer-to-peer) сетей (BitTorrent, eDonkey и т.д.).
4) Реклама всякого рода пресекается беспощадно.
5) Используйте тэг [CODE] для выделения кода программы (непременно с указанием языка программирования - выбрать из списка. В противном случае бессмысленно!). Уважайте тех, кто будет читать ваш код!
6) Если решение вашей проблемы найдено, то не забываем помечать тему специальной функцией "Вопрос решён". Вам всего лишь требуется при написании последнего ответа поставить одну единственную галочку прямо над формой ответа.
7) Если вы хотите получить совет для конкретной платформы/языка программирования, обязательно укажите их в вопросе

8) Если не прикрепляются/не скачиваются файлы, читаем Не прикрепляется / не скачивается файл. Любые обсуждения в данном разделе проблем с приложением файлов считаются оффтопиком! Со всеми вытекающими.

9) NEW! Уважаемые новички! Мы приветствуем Ваше желание научить всех посетителей раздела правильному программированию. Но огромная просьба, перед тем, как писать поучения в старых (последний ответ - "старее" месяца, а особенно, если вопрошавший не появляется на форуме уже не первый месяц, в чем можно убедиться в его профиле) темах, хорошо подумать, будет ли кому-нибудь, кроме Вас cамих, это интересно. Попытки накрутки количества тематических сообщений за счёт поднятия древних неактуальных тем ("некрофилия") будут наказываться по велению левой пятки модераторского состава (см. пп.12, 13 Правил)



Нарушение Правил может повлечь наказание со стороны модераторов.



user posted imageFAQ Раздела user posted imageПоиск в Разделе user posted imageMSDN Library Online | Ваше мнение о модераторах: user posted imageBarazuk user posted imageRikkie
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> waveInStop, waveInUnprepareHeader
    У меня 2 основных вопроса при организации записи с микрофона:

    1. Зачем нужна функция waveInStop, если есть waveInReset (и вообще, нужно ли использовать waveInStop перед/после waveInReset... и если да, то перед или после?)
    Либо наоборот: зачем waveInReset, если есть waveInStop (неужели просто ради того, чтобы вызвать callback-функцию с пустым буфером?)
    Функции waveOutStop же нет, зачем waveInStop?

    2. Зачем нужна функция waveInUnprepareHeader? Я спрашиваю не об описании функции, а о смысле её использования? Она что, освобождает какие области памяти или дескрипторы? В чём смысл её использования?
      Лучше тему перенести в этот раздел:
      Программирование звука
      Там быстрее ответят.
        Я, в общем-то, не против :)
          Тут на днях переделал модуль видео захвата, добавил автомат. Все эти плай, стой, ресет, опен, клоз.
          Думаю мысли мои совпадут с разработчиками.
          Так что далее мои домыслы. Насколько они верны не могу сказать.

          waveInStop
          waveInReset

          Согласно MSDN waveInReset отличается от waveInStop, тем что сбрасывает позиция в проигрываемом файле в 0.

          Источником звука может быть компакт диск и/или его проигыватель. А ещё лучше магнитная лента на кассете.

          В теории:
          Stop просто прекращает цикл приема. Барабан с плёнкой перестаёт крутиться. Но устройство продолжает быть открытым.
          Reset закрывает и открывает устройство. По сути это последовательность команд STOP CLOSE OPEN. Попутно около Open происходит обнуление всех внутренних буферов. Всех счётчиков, инициализация переменных в начальное значение. А значит наша катушка с плёнкой должна перемотаться назад в начальное(0) положение. А это время ну пусть в 8 раз быстрее времени воспроизведения. Т.е очень долго секунды или минуты. С диском проще несколько десятков мс.

          С точки зрения прикладного программиста, разницы нет. Разве что Reset будет работать дольше. Зато если вдруг произойдёт какая-то внутренняя ошибка устройства, драйвера и тп, то Reset сбросит, устранит её. А в случае STOP это не так. Как говориться 7 бед один Reset. У железячников, чипмейкеров reset по всюду! Начиная от простого тригера. А вот программисты с ним редко встречаются. Разве что при разработке автомата или машины состояний.

          На практике:
          Насколько знаю Майкрософт это так и не доделала. Так что по сути Stop и Reset у них это одно и тоже.


          waveOutStop - нету. Могли бы и сделать, но видимо сочли не нужным. Может быть сочли что OUT(вывод), не есть REC (запись). А значит вывод не надо останавливать. Такая игра слов.
          Вообще тут большой вопрос о том как строить конвейер кто им должен управлять. Толи конечное устройство в цепочке, толи начальное. Толи сторонний механизм.
          Я сторонник когда управляет конечное и далее по цепочки распространяется воздействие. Поэтому OutStop не должно существовать.
          Но у майкрософт тут все довольно сложно намешано. видимо хотели сделать что-бы прикладной программист мог выбирать кто будет управлять. Так что видимо waveOutStop должен был быть. Просто не доделали как и Reset.
          Когда я спроектировал свой конвейр с автоматом со всеми состояниями он у меня получился очень сложным, хотя до внедрения автомата был простым.


          Согласно MSDN waveInUnprepareHeader связана с функцией waveInPrepareHeader.
          В DirectX эта часть сделана гораздо проще.

          Ваше приложение говорит что буфер готов к записи и вызывает waveInPrepareHeader драйвер пишет данные в буфер, когда буфер заполнен в Header выставляется флаг Done.
          Приложение получает сообщение сигнал о том что буфер заполнен. Оно копирует данные из буфера. И выставляет флаг WHDR_PREPARED что буфер готов к следующей загрузки данных.

          Но тогда для чего нужна waveInUnprepareHeader? Все просто.

          Звуковая аппаратура работает с физическими адресами. А приложение с вируальными. При вызове waveInPrepareHeader ОС фиксирует страницы в памяти. В этих страницах лежат буферы данных. А при завершении приложении надо вызвать waveInUnprepareHeader чтобы сделать свободными.
          Если страница зафиксирована это значит что она не будет менять свои физические адреса, до тех пор пока вы не открепите её. А если её не зафиксировать, то в один момент драйвер может начать писать не туда.
          Зачем вообще странице менять адрес? К примеру свап страниц, когда ОС не хватает физической памяти она выгружает часть страниц на жёсткий диск.
          Есть и другие причины.

          А вообще такая вот фиксация была реализована в первых виндоусах через SharedMomory. Которая в свою очередь была ещё и ограниченным ресурсом. В том смысле что макрософт разрешала создавать только небольшие по тем временам объемы общей памяти.
          С выходом 3D Sound во времена эдак Win98, механизмы работы со звуком поменялись. А вот интерфейс остался старым. Так что сейчас waveInPrepareHeader и waveInUnprepareHeader это скорее атавизм, эдакий привет из 80-тых годов.
          Так что сейчас waveInPrepareHeader врятли выполняет фиксацию буфера, скорее всего сейчас она только установка флага WHDR_PREPARED и более ничего. И надобность в
          waveInUnprepareHeader - отпала.

          Не не думай-те что история закончилась в 90-тых. С 2000 по примерно 2010 года существовали мобильные телефоны, планшеты, карманные компьютеры.
          В них уж точно нет 3D Sound. А следовательно с большой долей вероятности в них использовалась старая схема с фиксацией буфера.

          Так что спешить выкидывать waveInUnprepareHeader не стоит.
          Сообщение отредактировано: Pavia -
            Pavia, весьма подробно. Спасибо :victory: ++
            В общем, буду считать, что waveInReset нужен тогда, когда имеет смысл вызывать callback-функцию, а waveInStop, когда нет :)

            p.s. Я тут подумал, что waveOutStop, возможно, не сделан потому, что waveInReset отменяет запись и возвращает значение длины записанного, равную нулю, т.е. по сути, запись в последний буфер не проиводится (поэтому смысл вызывать callback-функцию не всегда есть), а waveOutReset отмечает последний буфер как полностью проигранный (даже если это не так), тогда какой смысл делать waveOutStop ?
              WaveInStop приостанавливает запись. При этом текущий буфер(заголовок) в очереди помечается флагом done, а поле dwBytesRecorded содержит количество записанных байт до момента отработки WaveInStop. Приложение получит уведомление о том, что есть буфер, требующий обработки, через выбранный в waveinOpen механизм сallback. Все остальные, "пустые" буферы, остаются в очереди. Позиция записи сохраняется.
              WaveInReset стбрасывает позицию записи в ноль, выбрасывает все буферы из очереди записи(помечает их флагом done), приложению сыпятся уведомления по количеству буферов, и фактически устройство приводится к состоянию, как если бы оно было только что открыто функцией waveinopen.
              Цитата
              В общем, буду считать, что waveInReset нужен тогда, когда имеет смысл вызывать callback-функцию, а waveInStop, когда нет

              В ситуциии, когда в очереди записи находится хотя бы один буфер, callback механизм будет задействоан в любом случае. В случае stop приложению прилетит одно уведомление(нужно обработать один буфер), после reset прилетит столько сообщений, сколько буферов/заголовков было в очереди записи.


              Цитата
              нужно ли использовать waveInStop перед/после waveInReset

              Это независимые функции.
              Если хочешь приостановить запись(поставить на паузу) - waveinstop. Хотя можно использовать и waveinreset. зависит от логики программы.
              Если хочешь вообще прекратить запись(и затем, например, закрыть устройство, или сбросить устройство в исходное состояние) - waveinreset.
              Опять же, зависит от логики программы.

              waveinreset МОЖНО вызывать сразу после waveinstop(но зачем).
              Вызов waveinstop после waveinreset лишён смысла, так как очередь буферов после reset уже не существует, хотя ошибки не произойдёт, функция stop просто ничего не выполнит. "Посмотрит", что ей в этой ситуации делать нечего, подумает "сплю себе, никого не трогаю...", и вернёт MMSYSERR_NOERROR(ноль).


              Цитата
              waveInPrepareHeader/waveInUnprepareHeader

              Из того, что я где-то читал и интуитивно понял, функция prepare каким-то образом лочит область памяти(страницу памяти), не позволяя винде перемещать её(сбрасывать с своп?) и устанавливает соотвествущий флаг в заголовке. unprepare, cоответственно, разлочивает область памяти. Mайкрософт на этот предмет молчит как рыба об лёд.

              Первый вызов Waveinaddbuffer создаёт дополнительный поток, в котором происходит обработка сообщений wim_data, и вообще обработка очереди записи.
              waveinreset не только выбрасывает все буферы из очереди но и убивает сам поток. Поэтому Waveinunprepare нельзя вызыват из callback - функции после waveinreset. Этот момент обсуждался в разделе программирования звука, в какой теме не помню, не так давно(относительно).
              waveinstop "замораживает" процесс записи. Позволяя продолжить его вызовом waveinstart.
              Цитата

              У меня 2 основных вопроса при организации записи с микрофона

              Фунции wavin никак не связаны конкретно с микрофоном. Они осуществляют захват потока данных с выхода АЦП аудиокодека. Т.е., позволяют писать с любого входа/комбинации входов, в зависимости от настроек микшера.

              Добавлено
              Цитата
              Приложение получает сообщение сигнал о том что буфер заполнен. Оно копирует данные из буфера. И выставляет флаг WHDR_PREPARED что буфер готов к следующей загрузки данных.

              "Вручную" не следует трогать флаг WHDR_PREPARED. Т.е., кроме как обнулить перед вызовом waveinprepare/ Его выставляет подсистема записи как раз посредством waveinprepare. А что она делает с буфером/закоголовком помимо этого, это только смотреть реализацию функции.
              Сообщение отредактировано: Prince -
                Цитата
                Mайкрософт на этот предмет молчит как рыба об лёд.
                Не совсем. Вод старый документ. Но с тех пор аудио подсистема раза 2 переписывалась. Так что сейчас там может быть всё что угодно.

                Microsoft Windows NT 3.1 SDK

                MMRESULT waveInPrepareHeader(hWaveIn, lpwh, cbwh)

                HWAVEIN hWaveIn; /* handle of waveform device */
                LPWAVEHDR lpwh; /* address of structure with device information */
                UINT cbwh; /* size of structure, in bytes */


                The waveInPrepareHeader function prepares a buffer for waveform input.

                Parameter Description

                hWaveIn Identifies the waveform input device.
                lpwh Points to a WAVEHDR structure that identifies the buffer to be prepared.
                cbwh Specifies the size, in bytes, of the WAVEHDR structure.

                Returns

                If the function succeeds, the return value is zero; otherwise, it is an error code, which can be one of the following:

                Value Meaning

                MMSYSERR_INVALHANDLE The specified device handle is invalid.
                MMSYSERR_NOMEM The system is unable to allocate or lock memory.
                MMSYSERR_HANDLEBUSY The handle hWaveIn is in use on another thread.

                Comments

                The WAVEHDR structure and the data block pointed to by its lpData member must be allocated by using the GlobalAlloc function and the GMEM_MOVEABLE flag, and must be locked by using the GlobalLock function. Preparing a header that has already been prepared will have no effect, and the function will return zero.

                See Also

                waveInUnprepareHeader
                  Цитата Prince @
                  Т.е., кроме как обнулить перед вызовом waveinprepare
                  А оно вообще надо? Обнулять...

                  Добавлено
                  И надо ли сбрасывать флаг WHDR_DONE в заголовке при добавлении буфера (т.е. в callback-функции я добавляю тот же буфер без "переподготовки")?
                    Цитата
                    А оно вообще надо? Обнулять...

                    For either function, the dwFlags member must be set to zero.
                    Надо. Проверял когда-то.
                    Цитата
                    И надо ли сбрасывать флаг WHDR_DONE

                    Я не сбрасываю. Подозреваю, что waveinaddbuffer именно этот флаг не проверяет. И устанавливается он при заполнении буфера, перед посылкой уведомления приложению, независимо от его текущего значения.
                      Цитата Jin X @
                      оно вообще надо? Обнулять...

                      Head содержит указатель на то, сколько данных записалось и с какой позиции писать. dwBytesRecorded Так что обнулять надо.

                      https://msdn.microsoft.com/en-us/library/dd...7(v=vs.85).aspx

                      Цитата
                      Remarks

                      Use the WHDR_BEGINLOOP and WHDR_ENDLOOP flags in the dwFlags member to specify the beginning and ending data blocks for looping. To loop on a single block, specify both flags for the same block. Use the dwLoops member in the WAVEHDR structure for the first block in the loop to specify the number of times to play the loop.

                      The lpData, dwBufferLength, and dwFlags members must be set before calling the waveInPrepareHeader or waveOutPrepareHeader function. (For either function, the dwFlags member must be set to zero.)

                      Думаю тут ошибка. Для waveOutWrite и waveInAddBuffer
                      Буфер и заголовок должны быть подготовлены(Prepared).
                      Проще всего такую проверку сделать по флагу.
                      Цитата
                      WHDR_PREPARED Data buffer has been prepared by using the waveInPrepareHeader or waveOutPrepareHeader function. This flag is set by Windows.


                      Когда мы вызываем память она всегда выделяется не очищенной. И в ней может лежать мусор. Перед дальнейшем использованием её надо очистить от мусора. А затем надо заполнить разумными начальными данными.

                      Но стоит понимать что часть данных в WAVEHDR - системные, а часть прикладные. Системные заполняет система. И их изменять не надо, а другие надо.

                      Пользовательские.
                      lpData Points to the waveform data buffer.
                      dwBufferLength Specifies the length of the data buffer.
                      dwUser Specifies 32 bits of user data.
                      dwLoops Specifies the number of times to play the loop. This parameter is used only with output data buffers.

                      Системные.
                      lpNext Reserved; do not use.
                      reserved Reserved; do not use.
                      Эти рекомендую очистить перед первым вызовом waveInPrepareHeader или waveOutPrepareHeader


                      Смешанные:
                      dwBytesRecorded Specifies the amount of data in the buffer, if the header is used in input.
                      dwFlags Specifies flags that provide information about the data buffer. This member can be one or more of the following values:


                      dwBytesRecorded - пишет ОС. Читает ваше приложение.
                      Если ваше приложение начнёт писать. То предположительно запись начнётся, а вернее продолжится с указанно числа.

                      dwFlags - пользовательскими являются 2 и 3 биты отсчёт от 0.
                      Отвечающие за
                      WHDR_BEGINLOOP Buffer is the first buffer in a loop. This flag is used only with output data buffers.
                      WHDR_ENDLOOP Buffer is the last buffer in a loop. This flag is used only with output data buffers.

                      Остальные биты системные.
                      Из них 0 бит отвечающий за WHDR_DONE управляется драйвером звукового устройства.

                      Бит 1 отсчёт от 0 отвечающий за WHDR_PREPARED системный. За него отвечает виндоус.
                      Цитата
                      This flag is set by Windows.

                      Вы командуете виндоусу подготовить буфер при помощи вызова waveInPrepareHeader. И если виндоус его подготовли, то он отвечает что буфер готов, путем выставки этого флага.
                      На самом деле насколько понимаю его тоже выставляет драйвер.
                      Если вы сами начнёте двигать этот бит, то получите ошибки.


                      Цитата
                      И надо ли сбрасывать флаг WHDR_DONE

                      По моему надо. Иначе как драйвер узнает что приложение закончило работу с этим буфером и можно повторно его использовать? И при первичном вхождении тоже.

                      Добавлено
                      Цитата
                      И надо ли сбрасывать флаг WHDR_DONE в заголовке при добавлении буфера (т.е. в callback-функции я добавляю тот же буфер без "переподготовки")?


                      Цитата
                      For either function, the dwFlags member must be set to zero
                      По моему эти строчки как раз и относятся к вашему случаю. Кога Done выставлен. Дело в том что waveInPrepareHeader обнуляет этот флаг.
                      И функцию можно вызывать для уже подготовленных буферов. Ошибки не будет.
                      А вот если вы не вызываете waveInPrepareHeader, то соответственно за сброс бита WHDR_DONE должны отвечать вы сами.
                        Цитата
                        Иначе как драйвер узнает что приложение закончило работу с этим буфером и можно повторно его использовать?

                        По-моему, драйверу всё равно. При вызове waveinaddbuffer, драйверу нужен адрес заголовка и факт, что заголовок/буфер подготовлен функцией prepare. Флаг done устанавливается драйвером, но не проверяется. По моим наблюдениям.
                        Сообщение отредактировано: Prince -
                          Цитата Pavia @
                          Head содержит указатель на то, сколько данных записалось и с какой позиции писать. dwBytesRecorded Так что обнулять надо.
                          Что dwBytesRecorded ?

                          Цитата Pavia @
                          dwBytesRecorded - пишет ОС. Читает ваше приложение.
                          Если ваше приложение начнёт писать. То предположительно запись начнётся, а вернее продолжится с указанно числа.
                          Я в callback-функции не обнуляю dwBytesRecorded, а добавляю тот же буфер, при этом запись идёт в самого начала.

                          Цитата Pavia @
                          Эти рекомендую очистить перед первым вызовом waveInPrepareHeader или waveOutPrepareHeader

                          Цитата Pavia @
                          Системные.
                          lpNext Reserved; do not use.
                          reserved Reserved; do not use.
                          Эти рекомендую очистить перед первым вызовом waveInPrepareHeader или waveOutPrepareHeader
                          Зачем, если эти функции сами пишут туда что им надо?
                            Цитата Jin X @
                            Зачем, если эти функции сами пишут туда что им надо?

                            Официально функции waveInPrepareHeader или waveOutPrepareHeader устанавливают только 1 бит.
                            Поэтому что-бы избежать ошибки не инициированных данных. Нужно их проиницировать.

                            Цитата Jin X @
                            Я в callback-функции не обнуляю dwBytesRecorded, а добавляю тот же буфер, при этом запись идёт в самого начала.

                            Это нигде не написано. Более того такое поведение зависит от драйвера. А вот какой индус что там написал большой вопрос.
                            А может как в том анекдоте: Пешеход, не трамвай объедет. Водитель, не столб отойдёт. А в результате авария.

                            Всё что вы не контролируете рано или поздно вернётся к вам ошибками. Вот вы на всех драйверах проверили?
                            Мы тут как-то в соседней теме нашли что разные драйвера по разному обрабатывают TWAVEFORMATEX. Притом с точностью до наоборот :blink:
                            Код работающий в 1 драйвере не работал во 2. А код работающий во 2 не работал в 1.

                            как записать звук с микрофона в буфер? (сообщение #2669139)
                            Вначале думали что дело в ОС. Но потом выяснилось, что в драйвере. У одного звуковой чип был от RealTek у другого от VIA.

                            Чем больше вы контролируете при написании программы, тем проще вам будет на этапе отладке.
                            Сообщение отредактировано: Pavia -
                              Люди, вы загнались в каие-то дебри. Перед вызовом wavinprepareheader флаги заголовка должны быть обнулены.
                              При добавлении уже подготовленноо буфера в очередь записи, в колбек-фанкшн, флаги не нужно трогать(т.е., можно оставить флаг done как есть, на скорость ветра это не влияет).
                              Сообщение отредактировано: Prince -
                                Цитата Pavia @
                                Это нигде не написано.
                                А где написано, что dwBytesRecorded может обозначать что-либо кроме кол-ва записанных байт (т.е., например, позицию, с которой нужно начинать)?

                                Цитата Pavia @
                                Чем больше вы контролируете при написании программы, тем проще вам будет на этапе отладке.
                                Вот с этим согласен ;)
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0515 ]   [ 16 queries used ]   [ Generated: 19.03.24, 11:02 GMT ]