На главную Наши проекты:
Журнал   ·   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  все  ( Перейти к последнему сообщению )  
> Флаги на башнях... , Кто как выделяет память под звук?
    Какие флаги нужно ставить для выделенмя памяти под звуковые буферы функцией GlobalAlloc для WAVEHDR.lpData?
    Довольно часто встречается комбинация GMEM_MOVEABLE | GMEM_SHARE, что ине кажется нелогичным, т.к требует использования GlobalLock. Бывает и GMEM_FIXED | GMEM_SHARE и просто GMEM_FIXED, и в комбинацмм с GMEM_NODISCARD и GMEM_NOCOMPACT... Есть ли у кого какие наработки и/или соображения по теме? Чтобы работало во всех системах, а то в XP, W2000 работает, например, а в W98 грохнется.
      Если не изменяет память, то я использовал просто GMEM_FIXED.
        gena_dj
        А под какими виндами вы это тестировали? Под Win98 работает? GlobalLock использовали?
          Можно вообще не пользоваться GlobalAlloc/GlobalLock, а к примеру застолбить под это дело массив.
            ХЭМ
            Можно, только хочется чтобы размер буфера менять можно было. Можно, конечно склеивать/резать фиксированые, только гемор.
              Цитата DVK @
              А под какими виндами вы это тестировали? Под Win98 работает? GlobalLock использовали?

              Проверено под Win98, NT4.0 SP6, WinXP(разных сборок), Win2003 EE Server.

              В NT была проблема, которая не относится в флагам - в поле dwBytesRecorded (вроде так наз-ся) всегда приходило нулевое значение. Решил игнорировать и брать за основу исходный размер буфера. А в остальном - как часы. GlobalLock не нужно использовать - он применим только для обласлей с флагом GMEM_MOVEABLE.
                DVK
                Цитата
                хочется чтобы размер буфера менять можно было

                Тогда GetMem - и нет заморочки с флагами.
                  Цитата DVK @
                  только хочется чтобы размер буфера менять можно было

                  С какой целью? Может, можно по другому решить?
                    gena_dj
                    Спасибо за инфо.
                    Цитата gena_dj @
                    В NT была проблема, которая не относится в флагам

                    А может таки относится? С памятью шутки плохи. Черт знает где может глюк вылезти, вроде бы к
                    ней непосредственно не относящийся....
                    Цитата gena_dj @
                    GlobalLock не нужно использовать - он применим только для обласлей с флагом GMEM_MOVEABLE.

                    Да, так написано в SDK. Только на каком-то форуме, может даже здесь - не помню, кто-то жаловался, что эксепшен выскакивает с GMEM_FIXED. Ему привели пример из MSDN где GlobalLock было и проблема решилась вроде.

                    Цитата ХЭМ @
                    Тогда GetMem - и нет заморочки с флагами.

                    Ну, я тогда вообще под Delphi могу и SetLength использовать... Только эта память напрямую с драйвером связана и что там с ней будет... ИМХО нужно ее SHARE как минимум сделать. А GetMem - это ИМХО для внутреннего пользования, внутри приложения, в смысле.

                    Цитата gena_dj @
                    С какой целью? Может, можно по другому решить?

                    Хочу разрешение по частоте менять в процессе измерения моим LC-метром. Больше разрешение - длиннее буфер.
                    А что, кстати, массивы с фиксированой длиной нормально между приложениями использовать? Они по-определению SHARE?
                      Цитата DVK @
                      Хочу разрешение по частоте менять в процессе измерения моим LC-метром. Больше разрешение - длиннее буфер.

                      Изменять размер буфера после того, как выполнили waveInAddBuffer() - очень плохо, ошибки доступа к памяти могут возникнуть во время записи (winmm ведь не знает, что размер поменяли). Предлагаю следующую схему. В обычном режиме работаете с двумя буферами фиксированного размера, поочередно добавляя в очередь то первый, то второй. Когда возникнет необходимость использовать буфер большего размера - создаете новые 2 буфера(большего размера), добавляете его в очередь, а отработавшие старые - удаляете из очереди.

                      Цитата DVK @
                      А что, кстати, массивы с фиксированой длиной нормально между приложениями использовать? Они по-определению SHARE?

                      Имеются в виду статические массивы, объявляемые в приложении - если да, то это плохо. Они вообще в стеке лежат. Почему плохо - могу потом более подробно рассказать.

                      Я насколько понимаю, флаг SHARE вообще не нужен - WINMM загружается в адресное пространство данного процесса. Данный флаг нужен для передачи данных из одного процесса в другой. (Возможно, ошибаюсь. Предлагаю посмотреть у Рихтера.)

                      По поводу GlobalLock: лучше использовать только то, что написано в SDK, иначе могут быть проблемы с совместимостью.
                        Цитата gena_dj @
                        создаете новые 2 буфера(большего размера), добавляете его в очередь, а отработавшие старые - удаляете из очереди.


                        Можно и так, только мне с WaveOut-ом это нужно синхронизовать - там же тоже менять надо... Проще все закрыть и заново переоткрыть с новым размером. Ну потеряю 0.1 сек на фальш-буфер - не так страшно...

                        Цитата gena_dj @
                        Имеются в виду статические массивы, объявляемые в приложении


                        Да, я их и имел в виду. Просто встречал примеры в сети, где они использовались.

                        Цитата gena_dj @
                        Предлагаю посмотреть у Рихтера


                        А кто это?

                        Цитата gena_dj @
                        лучше использовать только то, что написано в SDK


                        Так кто-ж спорит...
                        Видел на каком-то форуме пример якобы из SDK для WaveIn так там было GMEM_MOVEABLE | GMEM_SHARE с GlobalLock -ом.
                        Сам нашёл там пример для WaveOut, так там с GMEM_FIXED...
                          Цитата
                          статические массивы

                          То, что данные хранить в стеке плохо - всем известно, зацикливаться на этом не будем. Когда их используешь, будь осторожен в следующей ситуации. Ошибка будет, если ты в некоторой функции определяешь такой массив, затем передаешь указатель на него в WINMM. Если не дождавшись завершения работы WINMM делаешь return, то может произойти подмена адреса возврата со всеми вытекающими последствиями.
                          Цитата
                          посмотреть у Рихтера

                          Джеффри РИХТЕР "Создание эффективных WIN32-приложений"

                          Ниже - некоторый код инициализации записи
                          ExpandedWrap disabled
                                if((stru->buf1=GlobalAlloc(GPTR,bsize))==NULL)//первый буфер для записи
                                {
                                    GlobalFree((HGLOBAL)stru);
                                    return NULL;
                                }
                                if((stru->buf2=GlobalAlloc(GPTR,bsize))==NULL)//второй буфер
                                {
                                    GlobalFree(stru->buf1);
                                    GlobalFree((HGLOBAL)stru);
                                    return NULL;
                                }
                             
                                stru->inFormat.wFormatTag = WAVE_FORMAT_PCM;
                                stru->inFormat.wBitsPerSample = BitsPerSample;
                                stru->inFormat.nChannels = Channels;
                                stru->inFormat.nSamplesPerSec = SampleFreq;
                                stru->inFormat.nBlockAlign = (stru->inFormat.wBitsPerSample + 7) / 8 * stru->inFormat.nChannels;
                                stru->inFormat.nAvgBytesPerSec = stru->inFormat.nSamplesPerSec * stru->inFormat.nBlockAlign;
                                stru->inFormat.cbSize=0;//описали формат записываемого звука
                             
                                if( waveInOpen ( &(stru->dev), stru->deviceID, &(stru->inFormat), (DWORD)waveInProc, (DWORD)stru, CALLBACK_FUNCTION ) !=MMSYSERR_NOERROR)
                                {
                                    GlobalFree(stru->buf1);
                                    GlobalFree(stru->buf2);
                                    GlobalFree((HGLOBAL)stru);
                                    return NULL;
                                }//открыли WinMM устройство, обращаю внимание на предпоследний и последний параметры
                            //CALLBACK_FUNCTION - значит, при возникновении событий будет вызываться waveInProc, ей будет передаваться в качестве аргумента структура stru
                             
                                waveInReset ( stru->dev );
                             
                                stru->wh1.lpData=(LPSTR)(stru->buf1);
                                stru->wh1.dwBufferLength=bsize;
                                stru->wh1.dwUser=0;
                                stru->wh1.dwFlags=0;
                                stru->wh1.dwLoops=0;
                                stru->wh1.lpNext=NULL;
                                stru->wh1.reserved=0;
                                if(waveInPrepareHeader(stru->dev,&(stru->wh1),sizeof(WAVEHDR))!= MMSYSERR_NOERROR)
                                {
                                    GlobalFree(stru->buf1);
                                    GlobalFree(stru->buf2);
                                    waveInClose(stru->dev);
                                    GlobalFree((HGLOBAL)stru);
                                    return NULL;
                                }//подготовили первый буфер, после этого уже нльзя делать GlobalReAlloc
                             
                                stru->wh2.lpData=(LPSTR)(stru->buf2);
                                stru->wh2.dwBufferLength=bsize;
                                stru->wh2.dwUser=0;
                                stru->wh2.dwFlags=0;
                                stru->wh2.dwLoops=0;
                                stru->wh2.lpNext=NULL;
                                stru->wh2.reserved=0;
                                if(waveInPrepareHeader(stru->dev,&(stru->wh2),sizeof(WAVEHDR))!= MMSYSERR_NOERROR)
                                {
                                    waveInUnprepareHeader(stru->dev,&(stru->wh1),sizeof(WAVEHDR));
                                    GlobalFree(stru->buf1);
                                    GlobalFree(stru->buf2);
                                    waveInClose(stru->dev);
                                    GlobalFree((HGLOBAL)stru);
                                    return NULL;
                                }//подготовили второй буфер
                             
                                if(waveInAddBuffer(stru->dev,&(stru->wh1),sizeof(WAVEHDR))!=MMSYSERR_NOERROR)
                                {
                                    waveInReset (stru->dev);
                                    waveInUnprepareHeader(stru->dev,&(stru->wh1),sizeof(WAVEHDR));
                                    waveInUnprepareHeader(stru->dev,&(stru->wh2),sizeof(WAVEHDR));
                                    GlobalFree(stru->buf1);
                                    GlobalFree(stru->buf2);
                                    waveInClose(stru->dev);
                                    GlobalFree((HGLOBAL)stru);
                                    DeleteCriticalSection ( &(stru->cs) );
                                    return NULL;
                                }//добавили первый буффер в очередь
                             
                                if(waveInAddBuffer(stru->dev,&(stru->wh2),sizeof(WAVEHDR))!=MMSYSERR_NOERROR)
                                {
                                    waveInReset (stru->dev);
                                    waveInUnprepareHeader(stru->dev,&(stru->wh1),sizeof(WAVEHDR));
                                    waveInUnprepareHeader(stru->dev,&(stru->wh2),sizeof(WAVEHDR));
                                    GlobalFree(stru->buf1);
                                    GlobalFree(stru->buf2);
                                    waveInClose(stru->dev);
                                    GlobalFree((HGLOBAL)stru);
                                    DeleteCriticalSection ( &(stru->cs) );
                                    return NULL;
                                }//добавили второй буффер в очередь
                             
                                stru->totalrec=0;//записано 0 буфферов
                                stru->lastblock=1;//последний записанный буфер - второй
                             
                                InitializeCriticalSection ( &(stru->cs));
                             
                                if(waveInStart(stru->dev)!=MMSYSERR_NOERROR)
                                {
                                    waveInReset (stru->dev);
                                    waveInUnprepareHeader(stru->dev,&(stru->wh1),sizeof(WAVEHDR));
                                    waveInUnprepareHeader(stru->dev,&(stru->wh2),sizeof(WAVEHDR));
                                    GlobalFree(stru->buf1);
                                    GlobalFree(stru->buf2);
                                    waveInClose(stru->dev);
                                    GlobalFree((HGLOBAL)stru);
                                    DeleteCriticalSection ( &(stru->cs) );
                                    return NULL;
                                }//начинаем запись


                          Ниже - функция обратного вызова, которая будет вызываться при возникновении событий во время записи
                          ExpandedWrap disabled
                            static void CALLBACK waveInProc(HWAVEIN hwi,
                                                            UINT uMsg,
                                                            DWORD dwInstance,
                                                            DWORD dwParam1,
                                                            DWORD dwParam2)
                            {
                                if ( uMsg == WIM_DATA )//записан буфер
                                {
                                    EnterCriticalSection ( &(((PRECSTRUCT)dwInstance)->cs) );
                                    (((PRECSTRUCT)dwInstance)->totalrec)++;//увеличиваем счетчик записанных буферов
                                    if(((PRECSTRUCT)dwInstance)->lastblock)
                                        ((PRECSTRUCT)dwInstance)->lastblock=0;
                                    else
                                        ((PRECSTRUCT)dwInstance)->lastblock=1;//индекс последнего записанного буфера
                                    LeaveCriticalSection ( &(((PRECSTRUCT)dwInstance)->cs) );
                                }
                            }


                          Если нужен код полностью - прошу написать в PM

                          Также прадлагаю посмотреть мой пост на тему "быстрой" записи
                          Сообщение отредактировано: gena_dj -
                            gena_dj
                            Интересный пример...
                            Некоторые комментарии.

                            1) Таки GPTR = GMEM_FIXED and GMEM_ZEROINIT.
                            Т.е. обнуляешь память - это обязательно?

                            2) Я, вообще, CALLBACK_WINDOW использую. Где-то читал, что в остальных CALLBACK-ах
                            кое-какие функции не работают. И проще это, а то всякие CriticalSection надо,
                            а я не знаю как с ними общаться, да и по ДОС-овски это как-то...

                            3) 2 буфера всего. У меня с 2 всё равно разрывы были. Хотя не знаю, может для
                            CALLBACK_FUNCTION + CriticalSection прокатывает. Я резервирую примерно на секунду
                            буферов, в любом случае не меньше 3-х.

                            4) Нету фальш-буфера. Опять же не знаю как с CALLBACK_FUNCTION, но у меня всё время
                            первый буфер в очереди заполнен нулями на неск. десятков миллисекунд. Поэтому я заготавливаю фальш-буфер
                            на 0.1 сек и запускаю его первым. Когда он возвращается я его отфильтровываю в функции обратного вызова,
                            и дальше он никуда не идёт. В результате в обработку идёт нормальный буфер без нулей в самом начале.

                            5) Зачем после waveInOpen вызываешь waveInReset?

                            Цитата gena_dj @
                            Также прадлагаю посмотреть мой пост на тему "быстрой" записи


                            Мне задержки в общем-то не так важны, главное - поток данных без разрывов.
                              Цитата DVK @
                              обнуляешь память - это обязательно?

                              не обязательно
                              Цитата DVK @
                              Я, вообще, CALLBACK_WINDOW использую

                              Это не всегда гуд - 1.приложение должно иметь окно, 2.сообщения должны быть доступны.
                              Цитата DVK @
                              CriticalSection...да и по ДОС-овски это как-то

                              Использую только для разграничения доступа с челью сохранения целостности данных при обращении к stru->totalrec и stru->lastblock
                              Не по-ДОСовски, это стандартная функция разграничения доступа при многопоточной работе. Почитать можно у Рихтера.
                              Цитата DVK @
                              для
                              CALLBACK_FUNCTION + CriticalSection прокатывает

                              прокатывает точно. Один буфер идет на запись, в то время как данные забираются у второго буфера.
                              Цитата DVK @
                              Нету фальш-буфера

                              Даже и не знаю: зачем он. Тишины в начале не наблюдал.
                              Цитата DVK @
                              Зачем после waveInOpen вызываешь waveInReset

                              Так рекомендуется делать. В SDK последовательность работы вроде так описана.
                                Цитата
                                Мне задержки в общем-то не так важны, главное - поток данных без разрывов.

                                При использовании CALLBACK_WINDOW были разрывы слышимые на звуке ( но я думаю, для LC метра это не принципиально). При переходе на CALLBACK_FUNCTION от разрывов удалось избавиться, но она работает с ограниченным набором функций (так написано в MSDN) и для того чтобы из нее вылезти пришлось воспользоваться PostMessage.
                                Вообще то LC-метр можно исполнить в одном приложении и не будет проблем, каким способом выделяешь память для буферов.
                                Цитата
                                Проще все закрыть и заново переоткрыть с новым размером. Ну потеряю 0.1 сек

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


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0506 ]   [ 15 queries used ]   [ Generated: 23.04.24, 20:18 GMT ]