Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.118.184.237] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Какие флаги нужно ставить для выделенмя памяти под звуковые буферы функцией GlobalAlloc для WAVEHDR.lpData?
Довольно часто встречается комбинация GMEM_MOVEABLE | GMEM_SHARE, что ине кажется нелогичным, т.к требует использования GlobalLock. Бывает и GMEM_FIXED | GMEM_SHARE и просто GMEM_FIXED, и в комбинацмм с GMEM_NODISCARD и GMEM_NOCOMPACT... Есть ли у кого какие наработки и/или соображения по теме? Чтобы работало во всех системах, а то в XP, W2000 работает, например, а в W98 грохнется. |
Сообщ.
#2
,
|
|
|
Если не изменяет память, то я использовал просто GMEM_FIXED.
|
Сообщ.
#3
,
|
|
|
gena_dj
А под какими виндами вы это тестировали? Под Win98 работает? GlobalLock использовали? |
Сообщ.
#4
,
|
|
|
Можно вообще не пользоваться GlobalAlloc/GlobalLock, а к примеру застолбить под это дело массив.
|
Сообщ.
#5
,
|
|
|
ХЭМ
Можно, только хочется чтобы размер буфера менять можно было. Можно, конечно склеивать/резать фиксированые, только гемор. |
Сообщ.
#6
,
|
|
|
Цитата DVK @ А под какими виндами вы это тестировали? Под Win98 работает? GlobalLock использовали? Проверено под Win98, NT4.0 SP6, WinXP(разных сборок), Win2003 EE Server. В NT была проблема, которая не относится в флагам - в поле dwBytesRecorded (вроде так наз-ся) всегда приходило нулевое значение. Решил игнорировать и брать за основу исходный размер буфера. А в остальном - как часы. GlobalLock не нужно использовать - он применим только для обласлей с флагом GMEM_MOVEABLE. |
Сообщ.
#7
,
|
|
|
DVK
Цитата хочется чтобы размер буфера менять можно было Тогда GetMem - и нет заморочки с флагами. |
Сообщ.
#8
,
|
|
|
Цитата DVK @ только хочется чтобы размер буфера менять можно было С какой целью? Может, можно по другому решить? |
Сообщ.
#9
,
|
|
|
gena_dj
Спасибо за инфо. Цитата gena_dj @ В NT была проблема, которая не относится в флагам А может таки относится? С памятью шутки плохи. Черт знает где может глюк вылезти, вроде бы к ней непосредственно не относящийся.... Цитата gena_dj @ GlobalLock не нужно использовать - он применим только для обласлей с флагом GMEM_MOVEABLE. Да, так написано в SDK. Только на каком-то форуме, может даже здесь - не помню, кто-то жаловался, что эксепшен выскакивает с GMEM_FIXED. Ему привели пример из MSDN где GlobalLock было и проблема решилась вроде. Цитата ХЭМ @ Тогда GetMem - и нет заморочки с флагами. Ну, я тогда вообще под Delphi могу и SetLength использовать... Только эта память напрямую с драйвером связана и что там с ней будет... ИМХО нужно ее SHARE как минимум сделать. А GetMem - это ИМХО для внутреннего пользования, внутри приложения, в смысле. Цитата gena_dj @ С какой целью? Может, можно по другому решить? Хочу разрешение по частоте менять в процессе измерения моим LC-метром. Больше разрешение - длиннее буфер. А что, кстати, массивы с фиксированой длиной нормально между приложениями использовать? Они по-определению SHARE? |
Сообщ.
#10
,
|
|
|
Цитата DVK @ Хочу разрешение по частоте менять в процессе измерения моим LC-метром. Больше разрешение - длиннее буфер. Изменять размер буфера после того, как выполнили waveInAddBuffer() - очень плохо, ошибки доступа к памяти могут возникнуть во время записи (winmm ведь не знает, что размер поменяли). Предлагаю следующую схему. В обычном режиме работаете с двумя буферами фиксированного размера, поочередно добавляя в очередь то первый, то второй. Когда возникнет необходимость использовать буфер большего размера - создаете новые 2 буфера(большего размера), добавляете его в очередь, а отработавшие старые - удаляете из очереди. Цитата DVK @ А что, кстати, массивы с фиксированой длиной нормально между приложениями использовать? Они по-определению SHARE? Имеются в виду статические массивы, объявляемые в приложении - если да, то это плохо. Они вообще в стеке лежат. Почему плохо - могу потом более подробно рассказать. Я насколько понимаю, флаг SHARE вообще не нужен - WINMM загружается в адресное пространство данного процесса. Данный флаг нужен для передачи данных из одного процесса в другой. (Возможно, ошибаюсь. Предлагаю посмотреть у Рихтера.) По поводу GlobalLock: лучше использовать только то, что написано в SDK, иначе могут быть проблемы с совместимостью. |
Сообщ.
#11
,
|
|
|
Цитата gena_dj @ создаете новые 2 буфера(большего размера), добавляете его в очередь, а отработавшие старые - удаляете из очереди. Можно и так, только мне с WaveOut-ом это нужно синхронизовать - там же тоже менять надо... Проще все закрыть и заново переоткрыть с новым размером. Ну потеряю 0.1 сек на фальш-буфер - не так страшно... Цитата gena_dj @ Имеются в виду статические массивы, объявляемые в приложении Да, я их и имел в виду. Просто встречал примеры в сети, где они использовались. Цитата gena_dj @ Предлагаю посмотреть у Рихтера А кто это? Цитата gena_dj @ лучше использовать только то, что написано в SDK Так кто-ж спорит... Видел на каком-то форуме пример якобы из SDK для WaveIn так там было GMEM_MOVEABLE | GMEM_SHARE с GlobalLock -ом. Сам нашёл там пример для WaveOut, так там с GMEM_FIXED... |
Сообщ.
#12
,
|
|
|
Цитата статические массивы То, что данные хранить в стеке плохо - всем известно, зацикливаться на этом не будем. Когда их используешь, будь осторожен в следующей ситуации. Ошибка будет, если ты в некоторой функции определяешь такой массив, затем передаешь указатель на него в WINMM. Если не дождавшись завершения работы WINMM делаешь return, то может произойти подмена адреса возврата со всеми вытекающими последствиями. Цитата посмотреть у Рихтера Джеффри РИХТЕР "Создание эффективных WIN32-приложений" Ниже - некоторый код инициализации записи 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; }//начинаем запись Ниже - функция обратного вызова, которая будет вызываться при возникновении событий во время записи 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 Также прадлагаю посмотреть мой пост на тему "быстрой" записи |
Сообщ.
#13
,
|
|
|
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 @ Также прадлагаю посмотреть мой пост на тему "быстрой" записи Мне задержки в общем-то не так важны, главное - поток данных без разрывов. |
Сообщ.
#14
,
|
|
|
Цитата DVK @ обнуляешь память - это обязательно? не обязательно Цитата DVK @ Я, вообще, CALLBACK_WINDOW использую Это не всегда гуд - 1.приложение должно иметь окно, 2.сообщения должны быть доступны. Цитата DVK @ CriticalSection...да и по ДОС-овски это как-то Использую только для разграничения доступа с челью сохранения целостности данных при обращении к stru->totalrec и stru->lastblock Не по-ДОСовски, это стандартная функция разграничения доступа при многопоточной работе. Почитать можно у Рихтера. Цитата DVK @ для CALLBACK_FUNCTION + CriticalSection прокатывает прокатывает точно. Один буфер идет на запись, в то время как данные забираются у второго буфера. Цитата DVK @ Нету фальш-буфера Даже и не знаю: зачем он. Тишины в начале не наблюдал. Цитата DVK @ Зачем после waveInOpen вызываешь waveInReset Так рекомендуется делать. В SDK последовательность работы вроде так описана. |
Сообщ.
#15
,
|
|
|
Цитата Мне задержки в общем-то не так важны, главное - поток данных без разрывов. При использовании CALLBACK_WINDOW были разрывы слышимые на звуке ( но я думаю, для LC метра это не принципиально). При переходе на CALLBACK_FUNCTION от разрывов удалось избавиться, но она работает с ограниченным набором функций (так написано в MSDN) и для того чтобы из нее вылезти пришлось воспользоваться PostMessage. Вообще то LC-метр можно исполнить в одном приложении и не будет проблем, каким способом выделяешь память для буферов. Цитата Проще все закрыть и заново переоткрыть с новым размером. Ну потеряю 0.1 сек Именно. |