Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[34.228.240.6] |
|
Сообщ.
#1
,
|
|
|
Начал с самого начала, попробовал вывести параметры устройств ввода своей звуковой карты. Код:
void getnumdevaudio(UINT &nin, UINT &nout) { nin = waveInGetNumDevs(); nout = waveOutGetNumDevs(); } void getinaudioinfo(UINT nin) { WAVEINCAPS wvinfo; for(UINT i = 0; i < nin; i++) { MMRESULT wvres = waveInGetDevCaps(i, &wvinfo, sizeof(wvinfo)); if(wvres != MMSYSERR_NOERROR) { std::cout << "Ошибка определения параметров устройства № " << i << std::endl; } // if(wvres _tprintf(TEXT("%s\t%i\t%i\n"), wvinfo.szPname, wvinfo.dwFormats, wvinfo.wChannels); } // for(UINT std::cin.get(); } // fun #include <windows.h> #include <iostream> #include <stdio.h> #include <string.h> #include <tchar.h> #include "mysound.h" void main(int argc, char* argv[]) { setlocale(LC_ALL, ""); UINT nin, nout; getnumdevaudio(nin, nout); getinaudioinfo(nin); } В результате получил: IA HD Audio CD In 786431 65535 VIA HD Audio Front Microphone 786431 65535 VIA HD Audio Line In 786431 65535 VIA HD Audio Microphone 786431 65535 VIA HD Audio Stereo Mixer 786431 65535 1. Почему wvinfo.wChannels дает большое целое значение, а не значение 1 или 2, как указано в MSDN? 2. Как из цифры, которые дает wvinfo.dwFormats, получить все возможные параметры устрройства ввода, например WAVE_FORMAT_1M08 и др., которые устройство ввода поддерживает? |
Сообщ.
#2
,
|
|
|
1. Потому что такое значение возвращает драйвер устройства. Почему производители посчитали, что он должен возвращать именно такое значение...да кто бы знал бы.
2. Проверять, установлен ли соотвествующий флаг в dwFormats. Цитата #DEFINE WAVE_FORMAT_1M08 0x00000001 #DEFINE WAVE_FORMAT_1S08 0x00000002 #DEFINE WAVE_FORMAT_1M16 0x00000004 #DEFINE WAVE_FORMAT_1S16 0x00000008 ... Имхо, не стоит dwFormats воспринимать слишком серьезно. "Стандартные комбинации" параметров давно стали общепринятыми, и современными картами(драйверами) поддерживаются. Кроме того, константы для форматов 96/192 кГц 24/32 бита и многоканальных, вряд ли существуют, судя по "конструкции" 32-х битного слова dwFormats. На мой взгляд, надежней проверять поддержку формата, открывая устройство с параметрами, валидность которых под вопросом. Цитата IA HD Audio CD In 786431 65535 VIA HD Audio Front Microphone 786431 65535 VIA HD Audio Line In 786431 65535 VIA HD Audio Microphone 786431 65535 VIA HD Audio Stereo Mixer 786431 65535 семерка? |
Сообщ.
#3
,
|
|
|
Нет, у меня Windows XP. Компьютер с какой-то навороченной мультимедийной звуковой картой. У меня возникли следующие вопросы:
1. Начинаю пробовать записать звук: int tryrec(UINT nin, DWORD nbuf) // nin = 0 (домашний компьтер с одним устройством ввода - микрофоном), nbuf = 1024 - размер буфера для записи звука { TCHAR serror[128]; serror[0] = 0; MMRESULT wvres; HWAVEIN hwvindev; WAVEFORMATEX wvformat; wvformat.wFormatTag = WAVE_FORMAT_PCM; wvformat.nChannels = 1; wvformat.nSamplesPerSec = 44100; wvformat.wBitsPerSample = 16; wvformat.nBlockAlign = wvformat.nChannels*wvformat.wBitsPerSample/8; wvformat.nAvgBytesPerSec = wvformat.nSamplesPerSec*wvformat.nBlockAlign; wvformat.cbSize = sizeof(wvformat); wvres = waveInOpen( &hwvindev, nin, &wvformat, 0, 0, WAVE_FORMAT_DIRECT); if(wvres != MMSYSERR_NOERROR) { printf("Устройство %i открыть не удалось!\n", nin); return 1; } // if(wvres char *swvbuf = (char*)malloc((size_t) nbuf); swvbuf[0] = 0; WAVEHDR wvbuf; wvbuf.lpData = swvbuf; wvbuf.dwBufferLength = nbuf; wvres = waveInPrepareHeader(hwvindev, &wvbuf, sizeof(wvbuf)); if(wvres != MMSYSERR_NOERROR) { printf("Не удалось подготовить буфер для записи!\n"); wvres = waveInGetErrorText(wvres, serror, 128); _tprintf(TEXT("Ошибка: %s\n"), serror); return 2; } // if(wvres return 0; } 2. Не очень понимаю механизм записи звука. Подготавливаю буфер, делаю waveInAddBuffer, тем самым уведомляю драйвер, что туда можно записывать данные. После заполнения буфера как мне в программе узнать, что он заполнился и надо его очистить и снова подготовить для новой записи? 3. Сколько может быть буферов и можно ли их записывать последовательно, а очищать и подготавливать за один прием после заполнения последнего? Или так не делается и нужно в процессе записи в следующий буфер готовить предыдущий? 4. Есть ли функция API для записи данных из буфера в файл? Или для записи нужно использовать обычные функции записи буфера swvbuf в файл? 5. Исходя из каких соображений нужно выбирать размер буфера? |
Сообщ.
#4
,
|
|
|
1. Скорее всего, ошибка в sizeof(wvbuf). Нужен размер заголовка sizeof(WAVEHDR), а не размер переменной.
2. Вы не указали механизм уведомления в waveinopen, что равносильно callback_null. В таком варианте вы никак не узнаете. Читайте про механизмы уведомлений звуковой подсистемы. 3. Сколько может быть буферов, я не знаю, много может быть. Когда буфер заполнен, вы получаете уведомление(в это время записывается следующий в очереди буфер). В зависимости от механизма уведомления, в оконной процедуре или в специальной функции, или в потоке, вы обрабатываете буфер, вернувшийся из очереди, и снова ставите в очередь при помощи waveinaddbuffer( или уничтожаете, если он вам уже не нужен). 4. Буфер - кусок памяти, начинающийся с адреса wvbuf.lpData. Для записи в файл можно использовать все доступные функции для работы с файлами. Для записи RIFF wave файла существуют специализированные функции mmio... 5. Исходя из потребностей. Маленький буфер обеспечит меньшую задержку и более быструю реакцию вашей программы, если ваша программа должна каким-то образом реагировать на параметры звука. Но, ваша программа не сможет реагировать на события происходящие в системе чаще, чем получает управление ваш процесс. Поэтому размер буфера снизу ограничен как минимум квантом времени виндовс. Если буфер оооочень большой, а ваш компьютер ооочень медленный, в этом случае комп может не успеть сделать с буфером все что необходимо до того, как из очереди вернется следующий буфер. Хоть и гипотетически, но такая ситуация возможна, в принципе. Сверху размер буфера ограничен объемом оперативной памяти и быстродействием компа. Если без "воды", размер 0.1 с - 1.0 с вполне нормально, имхо. |
Сообщ.
#5
,
|
|
|
Добавил в код
wvbuf.dwFlags = WHDR_PREPARED; |
Сообщ.
#6
,
|
|
|
Цитата wvbuf.dwFlags = WHDR_PREPARED; Неверно. Это флаг пользователь не устанавливает. Флаги можно просто обнулять. Сравните значения sizeof(wvbuf) и sizeof(WAVEHDR). |
Сообщ.
#7
,
|
|
|
Проверил размеры:
printf("%i\t%i\n", sizeof(WAVEHDR), sizeof(wvbuf)); wvbuf.dwFlags = WHDR_PREPARED; The lpData, dwBufferLength, and dwFlags members must be set before calling the waveInPrepareHeader or waveOutPrepareHeader function. Добавлено Мне не очень понятно по поводу буфера. Я задаю один буфер, его размеры и другие параметры. Достаточно указать только один буфер, а драйвер сам создаст нужное количество буферов? Или мне нужно завать количество буферов самому? Например, так: WAVEHDR wvbuf1, wvbuf2, wvbuf3 |
Сообщ.
#8
,
|
|
|
Цитата The lpData, dwBufferLength, and dwFlags members must be set before calling the waveInPrepareHeader or waveOutPrepareHeader function. The lpData, dwBufferLength, and dwFlags members of the WAVEHDR structure must be set before calling this function (dwFlags must be zero). Цитата Получилось, что они одинаковые (32) Значит, мое предположение было ошибочным. А если обнулить флаги? Цитата Мне не очень понятно по поводу буфера. Я задаю один буфер, его размеры и другие параметры. Достаточно указать только один буфер, а драйвер сам создаст нужное количество буферов? Или мне нужно завать количество буферов самому? Вы описываете все буферы. Не количество, а ВСЕ буферы. Как минимум необходимы два буфера. |
Сообщ.
#9
,
|
|
|
Спасибо за помощь. С обнуленным флагом все работает. Опять возникли вопросы (вызваны они наверное тем, что раньше с CALLBACK функциями в явном виде не приходилось работать):
1. У меня консольное приложение. В каком месте функции tryrec нужно описывать функцию уведомления? Непосредственно вместо нуля в определении функции waveInOpen или в виде отдельной фунции в заголовочном файле? В последнем случае в waveInOpen нужно указывать указатель на функцию уведомления? 2. Как организовать в консольном приложении оповещение о заполненности буферов? |
Сообщ.
#10
,
|
|
|
Цитата tumanovalex @ Как организовать в консольном приложении оповещение о заполненности буферов? Добавить в приложение функцию: //Процедура CallBack procedure CallBackZvyk(HWAVEx:THandle;UINT:Cardinal;Instance:DWORD; Param1:DWORD;Param2:DWORD);stdcall; var Hdr:PWaveHdr;I: Integer; begin if UINT = WOM_DONE then Err := WaveOutOpen(WaveOut, 0, @WaveFormat, Cardinal(@CallBackZvyk), 0, CALLBACK_FUNCTION); |
Сообщ.
#11
,
|
|
|
Видимо, я опять что-то не понял. Попытка использования функции окончилась неудачей. Сделал пустую функцию:
void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { switch(uMsg) { case WIM_OPEN: break; case WIM_DATA: break; case WIM_CLOSE: break; } } wvres = waveInOpen( &hwvindev, nin, &wvformat, (DWORD_PTR) (*waveInProc), 0, WAVE_FORMAT_DIRECT | СALLBACK_FUNCTION); |
Сообщ.
#12
,
|
|
|
Я не сишник. Возможно, проблема в dword_ptr вместо dword. Или в (DWORD_PTR) (*waveInProc). Без понятия.
Кстати, флаг WAVE_FORMAT_DIRECT не нужен, поскольку вы запрашиваете данные непосредственно от устройства (nin=0) и в формате WAVE_FORMAT_PCM. АСМ при любом раскладе задействован не будет. Если запрошенный формат не подерживается, устройство просто не удастся открыть. WaveInOpen завершится с ошибкой WAVERR_BADFORMAT. |
Сообщ.
#13
,
|
|
|
Цитата tumanovalex @ Подскажите, пожалуйста, как правильно вызывать CALLBACK функцию в моем случае. Цитата tumanovalex @ Видимо, я опять что-то не понял. Видимо.... Ты не будешь вызывать эту функцию. Ее будет вызывать СИСТЕМА. А для этого необходимо (в первую очередь) соблюсти СОГЛАШЕНИЕ О ВЫЗОВАХ данной функции. Это выполнено? Добавлено Цитата tumanovalex @ Вызов функции Это не ВЫЗОВ функции. Это всего лишь попытка ОТКРЫТЬ устройство с определенными параметрами. И судя по: Цитата tumanovalex @ окончился необработанным исключением параметры не соответствуют заявленным системой. |
Сообщ.
#14
,
|
|
|
Я неправильно сформулировал, имел в виду, как правильно записать свою функцию и сделать на нее ссылку в waveInOpen.
|
Сообщ.
#15
,
|
|
|
Как оказалось, ошибка возникает в строке
wvres = waveInStart(hwvindev); До начала записи я делаю следующее: char *swvbuf[16]; for(UINT i = 0; i < nbuf; i++) { swvbuf[i] = (char*)malloc((size_t) nbufsize); swvbuf[i][0] = 0; wvbuf.lpData = swvbuf[i]; wvbuf.dwFlags = 0; wvbuf.dwLoops = 0; wvbuf.dwUser = 0; wvbuf.dwBufferLength = nbufsize; wvres = waveInPrepareHeader(hwvindev, &wvbuf, sizeof(wvbuf)); errormes("Не удалось подготовить буфер для записи!\n", wvres); wvres = waveInAddBuffer(hwvindev, &wvbuf, sizeof(wvbuf)); errormes("Не удалось добавить буфер для записи!\n", wvres); } // for(UINT i = 0; i < nbuf; i++) Прикреплённый файлMySound.zip (9,77 Кбайт, скачиваний: 367) |
Сообщ.
#16
,
|
|
|
Привет, всем!
Решил не создавать новой темы, надеюсь, пишу в нужную ветку. На работе попросили разобраться с интерфейсом в mmystem. Вроде всё просто и понятно, но тут возникла сложность с регулировкой уровня звука и баланса: -используя самый простой метод, получаю доступ к первому попавшемуся destination, имеющий опцию контроля над уровнем громкости ("Общей громкости", в данном случае): ... if (mixerGetNumDevs() < 1) { return; } unsigned int rc; HMIXER hMix; if (mixerOpen(&hMix, 0, 0, 0, 0) != MMSYSERR_NOERROR) { return; } MIXERLINE ml = {0}; ml.cbStruct = sizeof(MIXERLINE); ml.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; if( (rc=mixerGetLineInfo(reinterpret_cast<HMIXEROBJ>(hMix), &ml, MIXER_GETLINEINFOF_DESTINATION)) != MMSYSERR_NOERROR) return; MIXERLINECONTROLS mlc = {0}; MIXERCONTROL mc = {0}; mlc.cbStruct = sizeof(MIXERLINECONTROLS); mlc.dwLineID = ml.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.pamxctrl = &mc; mlc.cbmxctrl = sizeof(MIXERCONTROL); if( (rc=mixerGetLineControls(reinterpret_cast<HMIXEROBJ>(hMix), &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE)) != MMSYSERR_NOERROR) return; - дальше начинаются непонимания: в инициализации структуры MIXERCONTROLDETAILS для получения данных я не могу понять, какой флаг указать, чтобы мне выдали информацию в структуре MIXERCONTROLDETAILS_UNSIGNED о уровне баланса? Хорошо, решил проэкспериментировать, заполнил объект MIXERCONTROLDETAILS следующим образом: ... MIXERCONTROLDETAILS_UNSIGNED* mxBalControlData = new MIXERCONTROLDETAILS_UNSIGNED[ml.cChannels*2]; MIXERCONTROLDETAILS mcd = {0}; mcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mcd.hwndOwner = 0; mcd.cMultipleItems = 0; mcd.dwControlID = mc.dwControlID; mcd.paDetails = mxBalControlData; mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); mcd.cChannels = ml.cChannels; if((rc=mixerGetControlDetails(reinterpret_cast<HMIXEROBJ>(hMix), &mcd, MIXER_GETCONTROLDETAILSF_VALUE)) != MMSYSERR_NOERROR) return; ... Конечно, я проверил в отладчике, что ml.cChannels равна 2, а значит, как я понимаю, имеется доступ к управлению громкости и баланса, ок. Но никак не пойму до конца, что за значения мне приходят в mxBalControlData: должны прийти показатели или уровня громкости, или баланса, - поэкперементировав чуток, пришёл к выводу, что приходят данные с того ползунка, показатели которого последний раз изменяли. И как же мне указать, что в данный момент мне нужен control показателя баланса или показателя громкости? |
Сообщ.
#17
,
|
|
|
Размер данных контрола должен быть 8 байт: 4 байта на канал(?);
Я сделал так: // volume:array[0..1] of integer; // volume[0] - левый канал // volume[1] - правый канал mxcdetail.cbDetails:=sizeof(MIXERCONTROLDETAILS_UNSIGNED)*Length(volume); mxcdetail.paDetails:=@volume; mixerGetControlDetails(mixercombobox.ItemIndex,@mxcdetail, MIXER_OBJECTF_MIXER or MIXER_GETCONTROLDETAILSF_VALUE); Как вариант, если не нужен именно master volume, достаточно waveoutsetvolume c предварительной проверкой флага WAVECAPS_LRVOLUME в waveoutgetdevcaps. . Контрол MIXERCONTROL_CONTROLTYPE_PAN пока не встречался. |
Сообщ.
#18
,
|
|
|
ту Prince
А кто из них кто? Левый канал, правый канал? Где будут показатели с уровня громкости, а где с уровня баланса? |
Сообщ.
#19
,
|
|
|
Цитата А кто из них кто? Левый канал, правый канал? Ну, чисто интутивно, первое 4-байтовое - левый канал, второе - правый. Но а кто мешает проверить? Цитата где будут показатели с уровня громкости, а где с уровня баланса? У вас в наличии два регулятора громкости, раздельно по левому и правому каналам, с пределами регулировки от 0 до 65535. А вам очень хочется общий регулятор громкости и баланса. Можно поступить так. Пределы регулировки громкости общего регулятора оставляете от 0 до 65535. А баланса от -100 до +100. -100 соотвествует левому крайнему положению слайдера баланса, +100 - крайнему правому. И считаете громкость в каналах при изменении положения регуляторов vol или bal(надеюсь, не напутал): case bal of -100..-1:Begin volume[0]:=vol; volume[1]:=trunc(vol*((100-abs(bal))/100); end; 0: Begin volume[0]:=vol; volume[1]:=vol; end; 1..100: Begin volume[0]:=trunc(vol*(100-bal)/100); volume[1]:=vol; end; end; Дальше передаете расчитанные значения в mixerSetControlDetails. Зависимость громкость/баланс можно придумать и другую, например, такую, чтобы при изменении баланса громкость изменялась синхронно в обоих каналах, все в ваших руках. А если вдруг в каком-нибудь из кодеков обнаружите контрол типа MIXERCONTROL_CONTROLTYPE_PAN, дайте знать, интересно. Все сказанное относится не только к линии master volume, но и к прочим. |
Сообщ.
#20
,
|
|
|
Цитата Prince @ Цитата А кто из них кто? Левый канал, правый канал? Ну, чисто интутивно, первое 4-байтовое - левый канал, второе - правый. Но а кто мешает проверить? Цитата где будут показатели с уровня громкости, а где с уровня баланса? У вас в наличии два регулятора громкости, раздельно по левому и правому каналам, с пределами регулировки от 0 до 65535. А вам очень хочется общий регулятор громкости и баланса. Можно поступить так. Пределы регулировки громкости общего регулятора оставляете от 0 до 65535. А баланса от -100 до +100. -100 соотвествует левому крайнему положению слайдера баланса, +100 - крайнему правому. И считаете громкость в каналах при изменении положения регуляторов vol или bal(надеюсь, не напутал): case bal of -100..1: Begin volume[0]:=vol; volume[1]:=trunc(vol*((100-abs(bal))/100); end; 0: Begin volume[0]:=vol; volume[1]:=vol; end; 1..100: Begin volume[0]:=trunc(vol*(100-bal)/100); volume[1]:=vol; end; end; Дальше передаете расчитанные значения в mixerSetControlDetails. Зависимость громкость/баланс можно придумать и другую, например, такую, чтобы при изменении баланса громкость изменялась синхронно в обоих каналах, все в ваших руках. А если вдруг в каком-нибудь из кодеков обнаружите контрол типа MIXERCONTROL_CONTROLTYPE_PAN, дайте знать, интересно. Все сказанное относится не только к линии master volume, но и к прочим. Ту Prince Т.е. если в массиве из двух элементов левому каналу соответсвует нулевой элемент, а правому - первый. То при заполнении данного массива функцией mixerGetControlDetails, как отличить какой элемент массива указывает на показания громкости, а какой на показания баланса? Будет ли нулевой элемент - уровнем громкости, а первый - уровнем баланса, как определяется порядок заполнения? |
Сообщ.
#21
,
|
|
|
Нету баланса, не-ту. Есть два канала громкости, левый/правый, каждому из них где-то во внутренностях аудиокодека(микросхема такая) соотвествуют некие регистры. А "баланс" вы высчитываете самоятоятельно из значений громкости каналов, возвращаемых драйвером. И наоборот, управляя балансом из своей программы, вы по значению "громкости" и "баланса" высчитываете значения громкости для каждого канала и передаете драйверу.
Посмотрите, сколько контролов имеет линия MIXERLINE_COMPONENTTYPE_DST_SPEAKERS и проверьте тип каждого контрола. С какой вы звуковушкой работаете? |
Сообщ.
#22
,
|
|
|
Цитата Prince @ Нету баланса, не-ту. Есть два канала громкости, левый/правый, каждому из них где-то во внутренностях аудиокодека(микросхема такая) соотвествуют некие регистры. А "баланс" вы высчитываете самоятоятельно из значений громкости каналов, возвращаемых драйвером. И наоборот, управляя балансом из своей программы, вы по значению "громкости" и "баланса" высчитываете значения громкости для каждого канала и передаете драйверу. Посмотрите, сколько контролов имеет линия MIXERLINE_COMPONENTTYPE_DST_SPEAKERS и проверьте тип каждого контрола. С какой вы звуковушкой работаете? Ааа) Понятно. А я всё думал , что они отдельно нам выдают уже суммарные данные по балансу, и по уровню громкости для двух каналов, а оно вон оно как. Ясно. А MIXERCONTROL_CONTROLTYPE_PAN у меня тоже не нашёл. Спасибо. |
Сообщ.
#23
,
|
|
|
Здравствуйте.
Пишу программу обработки сигналов, сигналы поступают на вход звуковой карты. Исходный код взят на просторах инета, думаю все кто начинал пользовать MMSystem его видели. Возникла следующая проблема: при получении уведомления запускается процедура обработки сигнала (начинаем анализировать данные буфера). Но в большинстве случаев начало буфера уже затерто новой порцией данных. Т.е. я полагаю что по достижении конца буфера драйвер (или что там пишет в него) посылает процедуре TForm1.OnWaveIn сообщение о заполнености буфера, переводит указатель на начало буфера и начинает писать свежие данные поверх старых. Скрин прилагаю. "Стык" может быть в любом месте, но обычно это происходит в начале буфера. Прикреплённый файлrewrite.jpg (44,91 Кбайт, скачиваний: 868) код Дельфи: procedure TForm1.StartButtonClick(Sender: TObject); var header: TWaveFormatEx; BufLen: integer; buf: pointer; begin with header do begin wFormatTag := WAVE_FORMAT_PCM; nChannels := 2; nSamplesPerSec := 22050; wBitsPerSample := 16; nBlockAlign := nChannels * (wBitsPerSample div 8); nAvgBytesPerSec := nSamplesPerSec * nBlockAlign; cbSize := 0; end; WaveInOpen(Addr(WaveIn), WAVE_MAPPER, addr(header),Form1.Handle, 0, CALLBACK_WINDOW); BufLen := header.nBlockAlign * BufSize; hBuf := GlobalAlloc(GMEM_MOVEABLE and GMEM_SHARE, BufLen); Buf := GlobalLock(hBuf); with BufHead do begin lpData := Buf; dwBufferLength := BufLen; dwFlags := WHDR_DONE;// изначально было WHDR_BEGINLOOP; dwLoops:=0; end; WaveInPrepareHeader(WaveIn, Addr(BufHead), sizeof(BufHead)); WaveInAddBuffer(WaveIn, addr(BufHead), sizeof(BufHead)); GetMem(pRight,BufSize * sizeof(TPoint)); GetMem(pLeft, BufSize * sizeof(TPoint)); Work := true; WaveInStart(WaveIn); end; сама процедура, которую вызываем procedure TForm1.OnWaveIn; var dSync,dBal,i,n: integer; data16: PData16; begin data16 := PData16(PWaveHdr(Msg.lParam)^.lpData); //всякие преобразования сигнала //If mono.Checked then for i:=0 to BufSize_1 do data16^[i*2+1]:=data16^[i*2]; //If Invert1.Checked then for i:=0 to BufSize_1 do data16^[i*2]:=-data16^[i*2]; //round(data16^[i*2]*(0.54-0.46*cos(2*Pi*I/BufSize))); //If Invert2.Checked then for i:=0 to BufSize_1 do data16^[i*2+1]:=-data16^[i*2+1]; //.. with PaintBox1.Canvas do begin Brush.Color := clWhite; FillRect(ClipRect); n:=(PaintBox1.Height div 2)+OffsetRCh.Position*2; for i:=0 to BufSize_1 do pRight^[i]:=Point(round(i*XScale),round(n-data16^[i*2+1]*YScale/YRCh.Position)); Polyline(Slice(pRight^, BufSize); n:=(PaintBox1.Height div 2)+OffsetLCh.Position*2; for i:=0 to BufSize_1 do pLeft^[i-StartN]:=Point(round(i*XScale),round(n-data16^[i*2]*YScale)); Polyline(Slice(pLeft^, BufSize); end; if Work then WaveInAddBuffer(WaveIn, PWaveHdr(Msg.lParam),SizeOf(TWaveHdr)) else Work := true; end; Вопрос - как запретить драйверу перезапись данных в буфере? Флаг dwLoops установил в 0 dwLoops:=0 Cпасибо. |
Сообщ.
#24
,
|
|
|
И да, ответ на возможный вопрос об изменении сигнала внутри процедуры TForm1.OnWaveIn - убирал все действия, кроме вывода на экран. Не помогает, стык все равно где-нибудь да есть.
|
Сообщ.
#25
,
|
|
|
Для обеспечения непрерывности потока данных необходимо использовать как минимум 2 буфера. Концепция waveform-audio предполагает очередь буферов. Почитайте темы пользователя tumanovalex, касающиеся работы с функциями mmsystem, в этом же разделе(а-а, эта одна из них ).
Цитата Т.е. я полагаю что по достижении конца буфера драйвер (или что там пишет в него) посылает процедуре TForm1.OnWaveIn сообщение о заполнености буфера, переводит указатель на начало буфера и начинает писать свежие данные поверх старых. "Виноват" не драйвер . Это Вы передаёте драйверу, в TForm1.OnWaveIn, только что заполненный буфер, а драйвер, естественно, снова начинает его заполнять, так как в очереди других буферов нет. А кроме того, что данные перезаписываются, они ещё и теряются, поскольку обработка сообщения системой, и собственно выполнение содержимого TForm1.OnWaveIn (до WaveInAddBuffer) занимают какое-то время. Добавлено dwLoops - для зацикливания буфера при воспроизведении. При записи, если не ошибаюсь, содержимое dwLoops не обрабатывается вообще. Обрабатывать буфер в оконной процедуре не совсем хорошо, лучше использовать механизмы уведомления CALLBACK_EVENT (в отдельном потоке), CALLBACK_FUNCTION, или CALLBACK_THREAD. |
Сообщ.
#26
,
|
|
|
Ок. Это сигнал с балансировочного станка, в одном канале датчики баланса, в другом - датчик синхронизации (фотодатчик). В непрерывности потока данных нужды нет никакой, потому использую один буфер. Требуется получить массив с неискаженными данными (т.е. наложение сигнала внутри буфера недопустимо), обработать его (вычислить сдвиг фазы), и только после этого получить следующий кусок данных.
Цитата "Виноват" не драйвер . Это Вы передаёте драйверу, в TForm1.OnWaveIn, только что заполненный буфер, а драйвер, естественно, снова начинает его заполнять, так как в очереди других буферов нет. Так буфер передается драйверу после процедуры рисования (которая выводит УЖЕ искаженный буфер). Или это не так? Цитата Обрабатывать буфер в оконной процедуре не совсем хорошо, лучше использовать механизмы уведомления CALLBACK_EVENT (в отдельном потоке), CALLBACK_FUNCTION, или CALLBACK_THREAD. к сожалению с потоками вообще не знаком Программирую на уровне "Хэллоу ворлд!" Можно ли в этом варианте написания программы "попросить" драйвер остановить запись в буфер при вызове OnWaveIn? Спасибо. |
Сообщ.
#27
,
|
|
|
Ок. Это сигнал с балансировочного станка, в одном канале датчики баланса, в другом - датчик синхронизации (фотодатчик). В непрерывности потока данных нужды нет никакой, потому использую один буфер. Требуется получить массив с неискаженными данными (т.е. наложение сигнала внутри буфера недопустимо), обработать его (вычислить сдвиг фазы), и только после этого получить следующий кусок данных.
Цитата "Виноват" не драйвер . Это Вы передаёте драйверу, в TForm1.OnWaveIn, только что заполненный буфер, а драйвер, естественно, снова начинает его заполнять, так как в очереди других буферов нет. А кроме того, что данные перезаписываются, они ещё и теряются, поскольку обработка сообщения системой, и собственно выполнение содержимого TForm1.OnWaveIn (до WaveInAddBuffer) занимают какое-то время. Так буфер передается драйверу после процедуры рисования (которая выводит УЖЕ искаженный буфер). Или это не так? Только что пробовал в самом начале OnWaveIn вставить WaveInStop(WaveIn). Теперь стык появляется вообще в любом месте. Цитата Обрабатывать буфер в оконной процедуре не совсем хорошо, лучше использовать механизмы уведомления CALLBACK_EVENT (в отдельном потоке), CALLBACK_FUNCTION, или CALLBACK_THREAD. к сожалению с потоками вообще не знаком Программирую на уровне "Хэллоу ворлд!" Можно ли в этом варианте написания программы "попросить" драйвер остановить запись в буфер при вызове OnWaveIn? Или сделать хоть что-нибудь, чтоб получить массив из 1024-16000 неискаженных точек. Спасибо. |
Сообщ.
#28
,
|
|
|
Цитата Для обеспечения непрерывности потока данных необходимо использовать как минимум 2 буфера. Концепция waveform-audio предполагает очередь буферов. нашел в сети вариант работы с 2мя буферами. Программа пишет данные со звуковой карты в файл. @wh1 и @wh2 - ссылки на буфера, при каждом вызове функции меняются между собой. Вроде красиво. Переделал под себя - не работает как надо. Видимо мой обработчик довольно долгий (выполняется преобразование Фурье, поиск в каждом канале сигнала "пиков", расчет фаз и вывод на экран) и буфер все равно успевает переполняться. // обработчик "данные готовы" procedure TformMain.MMProcData(var Message: TMessage); var temp: pWaveHdr; recorded: integer; begin temp:=address; if address=@wh1 then address:=@wh2 else address:=@wh1; // если не остановлено, ставим в очередь if not stop then waveInAddBuffer(hwi,address,sizeof(TWaveHdr)); recorded:=address.dwBytesRecorded; // записываем блок BlockWrite(fOut,(temp.lpData)^,recorded); n:=n+recorded; formMain.Label1.Caption:=IntToStr(n); end; |
Сообщ.
#29
,
|
|
|
Цитата Или это не так? В принципе, так. Причину "затирания" можно искать и найти, например, в отладчике. Или, сделайте копию буфера в начале OnWaveIn и сравните оригинал и копию перед вызовом waveinaddbuffer. Цитата Можно ли в этом варианте написания программы "попросить" драйвер остановить запись в буфер при вызове OnWaveIn? Или сделать хоть что-нибудь, чтоб получить массив из 1024-16000 неискаженных точек. Спасибо. Драйвер невиновен. Он выбрасывает из очереди заполненный буфер, уведомляет вас о том, что вот этот конкретный буфер уже заполнен, и вычёркивает его "из своей памяти". Хоть что-нибудь можно. Найти ошибку, и исправить. Цитата dwFlags := WHDR_DONE Флаги должны быть "по нулям". Флагами, как правило, распоряжается драйвер. Добавлено Цитата нашел в сети вариант работы с 2мя буферами. Да зачем вам вариант? В StartButtonClick добавьте ещё один буфер в очередь и всё. Цитата Видимо мой обработчик довольно долгий Видимо, где-то ошибка. |
Сообщ.
#30
,
|
|
|
Цитата Или, сделайте копию буфера в начале OnWaveIn и сравните оригинал и копию перед вызовом waveinaddbuffer. сделал и сравнил. Буфер в это время не меняется, даже sleep(100) вставлял в процедуру. |
Сообщ.
#31
,
|
|
|
Вывод: в буфере ничего не "затирается".
|
Сообщ.
#32
,
|
|
|
Переделал свою прг как вы советуете (добавил 2й буфер). Теперь стыков гораздо меньше, но они все равно проскакивают. Пока эту проблему решу анализом массива. Если стык есть - просто выюрасываем этот буфер и ждем следующего.
Спасибо! |
Сообщ.
#33
,
|
|
|
Цитата Теперь стыков гораздо меньше, но они все равно проскакивают. Запишите сигнал в звуковом редакторе. Если в нём присуствуют "стыки", значит, на входе звуковой карты такой сигнал. Вставьте в OnwaveIn запись буферов в файл, и таким способом запишите звук из своей программы. Проанализируйте запись в том же редакторе, сравните. Лучше найти причину глюка, и если она програмная, переписать программу по-человечески, чем костыли для игнорирования глюка изобретать. Имхо. |
Сообщ.
#34
,
|
|
|
Длительность буфера какая?
|
Сообщ.
#35
,
|
|
|
Ок. Сигнал был записан с помощью стандартной программы виндов "звукозапись", воспроизводилась через Винамп. Чтоб исключить возможную порчу звука Винампом сделано как вы советуете.
Из OnWaveIn выброшено все, добавлена только запись в файл. Используем 2 буфера, звук подаем на вход микрофона, я в него дышал, чтоб низкую частоту получить) После быстренько накалякал программу чтения из ВАВа. Читаем то, что записали в файл - Оппа, стыки идут! Прикреплённый файлtest.jpg (26 Кбайт, скачиваний: 691) |
Сообщ.
#36
,
|
|
|
Длительность (длина буфера) - 1024точки. Пробовал 2048 и т.д. до 16000 - не меняется ничего. Стык то есть, то нет. И если есть - то он обычно в начале буфера, со смещением 50-200 точек.
Частоту дискретизации ставил от 4000 до 44100. |
Сообщ.
#37
,
|
|
|
Из опыта, длительность буфера должна быть не менее 100 - 200 мс, иначе будут потери в данных.
Прикреплённый файлwavein.rar (12,04 Кбайт, скачиваний: 324) |
Сообщ.
#38
,
|
|
|
Цитата IvanB @ Переделал под себя - не работает как надо. Видимо мой обработчик довольно долгий (выполняется преобразование Фурье, поиск в каждом канале сигнала "пиков", расчет фаз и вывод на экран) и буфер все равно успевает переполняться. А не нужно все это делать в обработчике. Тем более, что сами пишете - непрерывный поток не нужен. Обработчик должен схватить буфер и либо скопировать его, скажем, в закольцованную очередь, либо выяснить, готов ли его кто обрабатывать, и если не готов - игнорировать. А программа обработки, соответственно, должна либо искать последний готовый буфер в очереди, освобождая все прочие в первом варианте, либо по завершении обработки выставлять флаг готовности и ждать, пока обработчик выплюнет очередной буфер. |
Сообщ.
#39
,
|
|
|
Цитата Обработчик должен схватить буфер и либо скопировать его, скажем, в закольцованную очередь, либо выяснить, готов ли его кто обрабатывать, и если не готов - игнорировать. Зачем ещё одна очередь, если она уже есть. |
Сообщ.
#40
,
|
|
|
andriano, так буфер в обработчике изначально находится в непотребном состоянии, с искажениями.
Только что проверил свою программу дома, на стационарном компе. Все работает как надо! Нет никаких наложений даже с одним буфером, при любой его длине (выставлял даже 256точек, 44100 дискретность). Но не работает нормально на ноутбуке на работе и на ноутбуке в гараже (в гараже старенький Тошиба, на работе свежий Асер). Такие вот пироги |
Сообщ.
#41
,
|
|
|
IvanB, вопрос, вероятно, не в том, стационарный комп или ноутбук, а в том, какой стоит звуковой чип и, соответственно, его возможности - что он делает аппаратно, а что перекладывает на процессор для программной обработки.
Любая правильно написанная программа надежно работает на любом железе. Если с Вашей программой этого не происходит, значит, она написана неправильно, а то, что она работает, говорит о том, что "железо" стационарного компа исправляет Ваши ошибки. Следовательно, Вы что-то изначально неверно организуете, и Вам нужно не удивляться, почему программа, которая кое-где работает, работает не везде, а исправлять ее так, чтобы она работала на наиболее неблагоприятной для нее конфигурации. "буфер в обработчике изначально находится в непотребном состоянии, с искажениями" - это неверная посылка. Если буфер у Вас оказывается в таком состоянии, значит, где-то Вы сами его портите. Либо что-то неправильное делаете с системой, что заставляет ее портить Ваш буфер. Добавлено Цитата Prince @ Зачем ещё одна очередь, если она уже есть. Затем, что вторая очередь не будет совпадать с первой. Они будут по-разному обрабатываться и их элементы будут по-разному освобождаться. |
Сообщ.
#42
,
|
|
|
Цитата Затем, что вторая очередь не будет совпадать с первой. Они будут по-разному обрабатываться и их элементы будут по-разному освобождаться. Так зачем это нужно? Чем не устривает та очередь, что уже есть? |
Сообщ.
#43
,
|
|
|
Цитата выставлял даже 256точек, 44100 дискретность Выставьте 256 точек(6 мс), 1024(23 мс), 2048(46 мс) и 4096(92 мс) и сравните загрузку процессора. Допустите, что ваша программа выполняется на старенькой машине, параллельно с другими задачами, тоже требующими процессорного времени. |
Сообщ.
#44
,
|
|
|
Prince, исправил в своей программе "инициализацию" буферов, что-то связанное с выделением памяти. Сделал как у вас в WaveIn.
До этого было так type BufHead1,BufHead2: TWaveHdr; adress:PWaveHdr; header: TWaveFormatEx; buf: pointer; СтартБаттонКлик begin with header do begin wFormatTag := WAVE_FORMAT_PCM; nChannels := 2; { количество каналов } nSamplesPerSec := SpS; { частота } wBitsPerSample := 16; { 8 / 16 бит } nBlockAlign := nChannels * (wBitsPerSample div 8); nAvgBytesPerSec := nSamplesPerSec * nBlockAlign; cbSize := 0; end; WaveInOpen(Addr(WaveIn), WAVE_MAPPER, addr(header),Form1.Handle, 0, CALLBACK_WINDOW); BufLen := header.nBlockAlign * BufSize; hBuf := GlobalAlloc(GMEM_MOVEABLE and GMEM_SHARE, BufLen); Buf := GlobalLock(hBuf); with BufHead1 do begin lpData := Buf; dwBufferLength := BufLen; dwFlags := 0;//WHDR_DONE;//BEGINLOOP; dwLoops:=0; end; adress:=@BufHead1; with BufHead2 do begin lpData := Buf; dwBufferLength := BufLen; dwFlags := 0;//WHDR_DONE;//BEGINLOOP; dwLoops:=0; end; WaveInPrepareHeader(WaveIn, Addr(BufHead1), sizeof(TWaveHdr)); WaveInPrepareHeader(WaveIn, Addr(BufHead2), sizeof(TWaveHdr)); WaveInAddBuffer(WaveIn, adress, sizeof(BufHead1)); WaveInStart(WaveIn); Переделал в такое with header do begin wFormatTag := WAVE_FORMAT_PCM; nChannels := 2; nSamplesPerSec := SpS; wBitsPerSample := 16; nBlockAlign := nChannels * (wBitsPerSample div 8); nAvgBytesPerSec := nSamplesPerSec * nBlockAlign; cbSize := 0; end; WaveInOpen(Addr(WaveIn), WAVE_MAPPER, addr(header),Form1.Handle, 0, CALLBACK_WINDOW); BufLen := header.nBlockAlign * BufSize; adress:=allocmem(sizeof(TWAVEHDR)); adress.lpData := allocmem(BufLen); adress.dwBufferLength := BufLen; WaveInPrepareHeader(WaveIn, adress, sizeof(TWaveHdr)); WaveInAddBuffer(WaveIn, adress, sizeof(TWaveHdr)); adress:=allocmem(sizeof(TWAVEHDR)); adress.lpData := allocmem(BufLen); adress.dwBufferLength := BufLen; WaveInPrepareHeader(WaveIn, adress, sizeof(TWaveHdr)); WaveInAddBuffer(WaveIn, adress, sizeof(TWaveHdr)); WaveInStart(WaveIn); Стало работать без ошибок! Вечером еще на старом буке надо проверить. Выделение буферов сейчас сделано как у вас в программе, здесь просто приведен пример того, что исправлено. Спасибо! зы: пока писал ответ, помоему понял ошибку. В первом случае я 1 раз выделил память (строки 21-22) и дальше писал по одному и тому же адресу 2 буфера. |
Сообщ.
#45
,
|
|
|
Доброго!
Писать звук с 2х каналов научили . Теперь хотелки выросли и нужно записать 4 канала на том же железе. Имеем встроенную звуковую систему АС97 на чипе Alc201, т.е. 2х канальное стерео. АЦП имеет частоту дискретизации 48кГц и 4 пары каналов записи. Задача - получить 4х канальный сигнал с полезной частотой не превышающей 3кГц, синхронизированный во времени. Идея следующая. Запрограммировать длину буфера весьма короткой (1, 2 или 4 отсчета). Получить от АЦП выборку 1й пары каналов, скинуть ее в буфер1. Потом переключить запись на другую пару каналов и получить выборку с нее, сбросить ее в буфер2. Получится ли таким образом записать "одновременно" 4 канала? Или потрачу много времени на переключение каналов? |