Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[174.129.59.198] |
|
Страницы: (3) 1 [2] 3 все ( Перейти к последнему сообщению ) |
Сообщ.
#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) вставлял в процедуру. |