На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania 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
Модераторы: barazuk
Страницы: (3) 1 [2] 3  все  ( Перейти к последнему сообщению )  
> Работа со звуком с помощью MMSystem
    Привет, всем!
    Решил не создавать новой темы, надеюсь, пишу в нужную ветку.
    На работе попросили разобраться с интерфейсом в 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 показателя баланса или показателя громкости?
      Размер данных контрола должен быть 8 байт: 4 байта на канал(?);
      Я сделал так:

      ExpandedWrap disabled
        // 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 пока не встречался.
      Сообщение отредактировано: Prince -
      Человек человека понять не может.
        ту Prince
        А кто из них кто? Левый канал, правый канал? Где будут показатели с уровня громкости, а где с уровня баланса?
          Цитата
          А кто из них кто? Левый канал, правый канал?

          Ну, чисто интутивно, первое 4-байтовое - левый канал, второе - правый. Но а кто мешает проверить? :rolleyes:
          Цитата
          где будут показатели с уровня громкости, а где с уровня баланса?

          У вас в наличии два регулятора громкости, раздельно по левому и правому каналам, с пределами регулировки от 0 до 65535.
          А вам очень хочется общий регулятор громкости и баланса.
          Можно поступить так.
          Пределы регулировки громкости общего регулятора оставляете от 0 до 65535.
          А баланса от -100 до +100.
          -100 соотвествует левому крайнему положению слайдера баланса, +100 - крайнему правому.
          И считаете громкость в каналах при изменении положения регуляторов vol или bal(надеюсь, не напутал):
          ExpandedWrap disabled
            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 -
          Человек человека понять не может.
            Цитата Prince @
            Цитата
            А кто из них кто? Левый канал, правый канал?

            Ну, чисто интутивно, первое 4-байтовое - левый канал, второе - правый. Но а кто мешает проверить? :rolleyes:
            Цитата
            где будут показатели с уровня громкости, а где с уровня баланса?

            У вас в наличии два регулятора громкости, раздельно по левому и правому каналам, с пределами регулировки от 0 до 65535.
            А вам очень хочется общий регулятор громкости и баланса.
            Можно поступить так.
            Пределы регулировки громкости общего регулятора оставляете от 0 до 65535.
            А баланса от -100 до +100.
            -100 соотвествует левому крайнему положению слайдера баланса, +100 - крайнему правому.
            И считаете громкость в каналах при изменении положения регуляторов vol или bal(надеюсь, не напутал):
            ExpandedWrap disabled
              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, как отличить какой элемент массива указывает на показания громкости, а какой на показания баланса? Будет ли нулевой элемент - уровнем громкости, а первый - уровнем баланса, как определяется порядок заполнения?
              Нету баланса, не-ту. Есть два канала громкости, левый/правый, каждому из них где-то во внутренностях аудиокодека(микросхема такая) соотвествуют некие регистры. А "баланс" вы высчитываете самоятоятельно из значений громкости каналов, возвращаемых драйвером. И наоборот, управляя балансом из своей программы, вы по значению "громкости" и "баланса" высчитываете значения громкости для каждого канала и передаете драйверу.

              Посмотрите, сколько контролов имеет линия MIXERLINE_COMPONENTTYPE_DST_SPEAKERS и проверьте тип каждого контрола. С какой вы звуковушкой работаете?
              Сообщение отредактировано: Prince -
              Человек человека понять не может.
                Цитата Prince @
                Нету баланса, не-ту. Есть два канала громкости, левый/правый, каждому из них где-то во внутренностях аудиокодека(микросхема такая) соотвествуют некие регистры. А "баланс" вы высчитываете самоятоятельно из значений громкости каналов, возвращаемых драйвером. И наоборот, управляя балансом из своей программы, вы по значению "громкости" и "баланса" высчитываете значения громкости для каждого канала и передаете драйверу.

                Посмотрите, сколько контролов имеет линия MIXERLINE_COMPONENTTYPE_DST_SPEAKERS и проверьте тип каждого контрола. С какой вы звуковушкой работаете?

                Ааа) Понятно. А я всё думал :wall: , что они отдельно нам выдают уже суммарные данные по балансу, и по уровню громкости для двух каналов, а оно вон оно как. Ясно.
                А MIXERCONTROL_CONTROLTYPE_PAN у меня тоже не нашёл.
                Спасибо.
                  Здравствуйте.
                  Пишу программу обработки сигналов, сигналы поступают на вход звуковой карты. Исходный код взят на просторах инета, думаю все кто начинал пользовать MMSystem его видели. Возникла следующая проблема: при получении уведомления запускается процедура обработки сигнала (начинаем анализировать данные буфера). Но в большинстве случаев начало буфера уже затерто новой порцией данных. Т.е. я полагаю что по достижении конца буфера драйвер (или что там пишет в него) посылает процедуре TForm1.OnWaveIn сообщение о заполнености буфера, переводит указатель на начало буфера и начинает писать свежие данные поверх старых.
                  Скрин прилагаю. "Стык" может быть в любом месте, но обычно это происходит в начале буфера.
                  Прикреплённый файлПрикреплённый файлrewrite.jpg (44,91 Кбайт, скачиваний: 199)

                  код Дельфи:
                  ExpandedWrap disabled
                    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;


                  сама процедура, которую вызываем
                  ExpandedWrap disabled
                    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пасибо.
                    И да, ответ на возможный вопрос об изменении сигнала внутри процедуры TForm1.OnWaveIn - убирал все действия, кроме вывода на экран. Не помогает, стык все равно где-нибудь да есть.
                      Для обеспечения непрерывности потока данных необходимо использовать как минимум 2 буфера. Концепция waveform-audio предполагает очередь буферов. Почитайте темы пользователя tumanovalex, касающиеся работы с функциями mmsystem, в этом же разделе(а-а, эта одна из них :lol: ).
                      Цитата
                      Т.е. я полагаю что по достижении конца буфера драйвер (или что там пишет в него) посылает процедуре TForm1.OnWaveIn сообщение о заполнености буфера, переводит указатель на начало буфера и начинает писать свежие данные поверх старых.

                      "Виноват" не драйвер . Это Вы передаёте драйверу, в TForm1.OnWaveIn, только что заполненный буфер, а драйвер, естественно, снова начинает его заполнять, так как в очереди других буферов нет.
                      А кроме того, что данные перезаписываются, они ещё и теряются, поскольку обработка сообщения системой, и собственно выполнение содержимого TForm1.OnWaveIn (до WaveInAddBuffer) занимают какое-то время.

                      Добавлено
                      dwLoops - для зацикливания буфера при воспроизведении. При записи, если не ошибаюсь, содержимое dwLoops не обрабатывается вообще.

                      Обрабатывать буфер в оконной процедуре не совсем хорошо, лучше использовать механизмы уведомления CALLBACK_EVENT (в отдельном потоке), CALLBACK_FUNCTION, или CALLBACK_THREAD.
                      Сообщение отредактировано: Prince -
                      Человек человека понять не может.
                        Ок. Это сигнал с балансировочного станка, в одном канале датчики баланса, в другом - датчик синхронизации (фотодатчик). В непрерывности потока данных нужды нет никакой, потому использую один буфер. Требуется получить массив с неискаженными данными (т.е. наложение сигнала внутри буфера недопустимо), обработать его (вычислить сдвиг фазы), и только после этого получить следующий кусок данных.

                        Цитата
                        "Виноват" не драйвер . Это Вы передаёте драйверу, в TForm1.OnWaveIn, только что заполненный буфер, а драйвер, естественно, снова начинает его заполнять, так как в очереди других буферов нет.

                        Так буфер передается драйверу после процедуры рисования (которая выводит УЖЕ искаженный буфер). Или это не так?

                        Цитата
                        Обрабатывать буфер в оконной процедуре не совсем хорошо, лучше использовать механизмы уведомления CALLBACK_EVENT (в отдельном потоке), CALLBACK_FUNCTION, или CALLBACK_THREAD.

                        к сожалению с потоками вообще не знаком :whistle: Программирую на уровне "Хэллоу ворлд!" Можно ли в этом варианте написания программы "попросить" драйвер остановить запись в буфер при вызове OnWaveIn?
                        Спасибо.
                          Ок. Это сигнал с балансировочного станка, в одном канале датчики баланса, в другом - датчик синхронизации (фотодатчик). В непрерывности потока данных нужды нет никакой, потому использую один буфер. Требуется получить массив с неискаженными данными (т.е. наложение сигнала внутри буфера недопустимо), обработать его (вычислить сдвиг фазы), и только после этого получить следующий кусок данных.

                          Цитата
                          "Виноват" не драйвер . Это Вы передаёте драйверу, в TForm1.OnWaveIn, только что заполненный буфер, а драйвер, естественно, снова начинает его заполнять, так как в очереди других буферов нет. А кроме того, что данные перезаписываются, они ещё и теряются, поскольку обработка сообщения системой, и собственно выполнение содержимого TForm1.OnWaveIn (до WaveInAddBuffer) занимают какое-то время.

                          Так буфер передается драйверу после процедуры рисования (которая выводит УЖЕ искаженный буфер). Или это не так? Только что пробовал в самом начале OnWaveIn вставить WaveInStop(WaveIn). Теперь стык появляется вообще в любом месте.

                          Цитата
                          Обрабатывать буфер в оконной процедуре не совсем хорошо, лучше использовать механизмы уведомления CALLBACK_EVENT (в отдельном потоке), CALLBACK_FUNCTION, или CALLBACK_THREAD.

                          к сожалению с потоками вообще не знаком :whistle: Программирую на уровне "Хэллоу ворлд!" Можно ли в этом варианте написания программы "попросить" драйвер остановить запись в буфер при вызове OnWaveIn? Или сделать хоть что-нибудь, чтоб получить массив из 1024-16000 неискаженных точек.
                          Спасибо.
                            Цитата
                            Для обеспечения непрерывности потока данных необходимо использовать как минимум 2 буфера. Концепция waveform-audio предполагает очередь буферов.

                            нашел в сети вариант работы с 2мя буферами. Программа пишет данные со звуковой карты в файл.
                            @wh1 и @wh2 - ссылки на буфера, при каждом вызове функции меняются между собой. Вроде красиво. Переделал под себя - не работает как надо. Видимо мой обработчик довольно долгий (выполняется преобразование Фурье, поиск в каждом канале сигнала "пиков", расчет фаз и вывод на экран) и буфер все равно успевает переполняться.
                            ExpandedWrap disabled
                              // обработчик "данные готовы"
                              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;
                              Цитата
                              Или это не так?

                              В принципе, так.
                              Причину "затирания" можно искать и найти, например, в отладчике.
                              Или, сделайте копию буфера в начале OnWaveIn и сравните оригинал и копию перед вызовом waveinaddbuffer.
                              Цитата
                              Можно ли в этом варианте написания программы "попросить" драйвер остановить запись в буфер при вызове OnWaveIn? Или сделать хоть что-нибудь, чтоб получить массив из 1024-16000 неискаженных точек.
                              Спасибо.

                              Драйвер невиновен. Он выбрасывает из очереди заполненный буфер, уведомляет вас о том, что вот этот конкретный буфер уже заполнен, и вычёркивает его "из своей памяти".
                              Хоть что-нибудь можно. Найти ошибку, и исправить.
                              Цитата
                              dwFlags := WHDR_DONE

                              Флаги должны быть "по нулям". Флагами, как правило, распоряжается драйвер.

                              Добавлено
                              Цитата
                              нашел в сети вариант работы с 2мя буферами.

                              Да зачем вам вариант? В StartButtonClick добавьте ещё один буфер в очередь и всё.
                              Цитата
                              Видимо мой обработчик довольно долгий

                              Видимо, где-то ошибка.
                              Человек человека понять не может.
                                Цитата
                                Или, сделайте копию буфера в начале OnWaveIn и сравните оригинал и копию перед вызовом waveinaddbuffer.

                                сделал и сравнил. Буфер в это время не меняется, даже sleep(100) вставлял в процедуру.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (3) 1 [2] 3  все


                                Рейтинг@Mail.ru
                                [ Script Execution time: 0,1793 ]   [ 19 queries used ]   [ Generated: 22.09.17, 13:27 GMT ]