Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.225.31.159] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Люди добрые, подскажите пожалуйста как реализовать захват звука через directsound для дальнейшей обработки (БПФ, умножение на sin/cos и тому подобное)
|
Сообщ.
#3
,
|
|
|
вот у меня есть образец, только не могу в нем разобраться:
//Подготовка открытия устройства записи звука HWAVEIN hWaveIn; HWND hWnd = NULL; WAVEFORMATEX WaveFormat; WAVEHDR WaveHdr; //Заполнение структуры WAVEFORMATEX WaveFormat.wFormatTag = WAVE_FORMAT_PCM; WaveFormat.nChannels = 2; WaveFormat.nSamplesPerSec = 44100; WaveFormat.wBitsPerSample = 16; WaveFormat.nBlockAlign = (WaveFormat.nChannels*WaveFormat.wBitsPerSample)/8; WaveFormat.nAvgBytesPerSec = WaveFormat.nSamplesPerSec*WaveFormat.nBlockAlign; //WaveFormat.cbSize = 0; для формата РСМ строка не нужна //Открытие устройства записи звука MMRESULT mmRes = waveInOpen(&hWaveIn, WAVE_MAPPER, &WaveFormat, (DWORD)hWnd, 0L, CALLBACK_WINDOW); //нижние 3 строки можно и нужно выкинуть. Нужны только на этапе отладки. //При успехе-сообщение: "Указанная команда выполнена" char buff[100]; waveInGetErrorText(mmRes, buff, 100); ShowMessage(buff); //------------------------------ //подготовка буфера для записи int t = 1; //время записи в секундах int BufferSize = 0; //Заполнение структуры WAVEHDR BufferSize = WaveFormat.nBlockAlign*WaveFormat.nSamplesPerSec*t; WaveHdr.lpData = (char*)malloc(BufferSize); WaveHdr.dwBufferLength = BufferSize; WaveHdr.dwFlags = 0; waveInPrepareHeader(hWaveIn, &WaveHdr, sizeof(WAVEHDR)); waveInAddBuffer(hWaveIn, &WaveHdr, sizeof(WAVEHDR)); //Начало записи звука. Источник звука определяется //настройками аудиопараметров (рег. громкости в Tray) mmRes = waveInStart(hWaveIn); //нижние 2 строки можно и нужно выкинуть. Нужны только на этапе отладки. //При успехе-сообщение: "Указанная команда выполнена" waveInGetErrorText(mmRes, buff, 100); ShowMessage(buff); //------------------------------------ // MM_WIM_DATA; строка оказалась не работающей waveInUnprepareHeader(hWaveIn, &WaveHdr, sizeof(WAVEHDR)); //WaveHdr.lpData - указывают на буфер, где хранять записанные данные. //После вызова waveInUnprepareHeader ими можно распоряжаться //по собственному усмотрению //------------------------------------- //так мы и сделаем, перепишем содержимое буфера в бинарный файл int Hndl = FileCreate("d:\\Sound1.bin"); if(Hndl == -1) Application->MessageBox("Не могу открыть файло", "File Error", MB_OK); else { FileWrite(Hndl, WaveHdr.lpData, WaveHdr.dwBufferLength); FileClose(Hndl); } //--------------------------------------------- //а теперь сбросим устройство записи waveInReset(hWaveIn); //и освободим память из-под буфера free(WaveHdr.lpData); //По окончанию записи следует закрыть аудиоустройство: waveInClose(hWaveIn); //нижние 2 строки можно и нужно выкинуть. Нужны только на этапе отладки. //При успехе-сообщение: "Указанная команда выполнена" waveInGetErrorText(mmRes, buff, 100); ShowMessage(buff); } //--------------------------------------------------------------------------- что нужно сделать чтоб получать непрерывный сигнал для обработки? |
Сообщ.
#4
,
|
|
|
Этот код использует ММЕ интерфейс, а не DS. Только Ваш образец нужно основательно обработать напильником. Если Вам нужен ММЕ вариант захвата звука, вопрос не раз обсуждался.
http://yandex.ru/yandsearch?text=waveinope...urces.ru&lr=187 |
Сообщ.
#5
,
|
|
|
ступил... бывает. Вообщем в конечном итоге у меня должен получиться демодулятор сигнала ЧТ. Со звуком никогда раньше не работал. Как можно организовать наиболее эфективно захват звука со входа звуковой карты? Все это нужно организовать на с++ builder
|
Сообщ.
#6
,
|
|
|
Цитата Вообщем в конечном итоге у меня должен получиться демодулятор сигнала ЧТ. Со звуком никогда раньше не работал. Тогда нужно разбираться с образцом. |
Сообщ.
#7
,
|
|
|
Поскольку автор темы потерялся, я, пожалуй, продолжу тему о захвате звука с помощью интерфейса DirectSound. На delphi.
Исправления и дополнения, как всегда, приветствуются. Почему-то во всех источниках( например упомянутый http://www.helloworld.ru/texts/comp/games/...sound/index.htm), которые по видимому, являются вариантами одного и того же источника, упоминается о VxD драйверах, Win98, NT, и о том, что функции DirectSoundCapture эмулируются через ММЕ. Если я правильно понял, всё это в прошлом. Для записи используется устройство по умолчанию, но поскольку GUID-ы всех устройств при переборе заносятся в массив GUIDList, а ТDSRecorder имеет свойство GUID, добавить строчку в подпрограмму StartRecord труда не составит. Метод определения текущей позиции для чтения данных из буфера может быть и другим, в частности я не разбирался с GetCurrentPosition. А эвенты для нотификации позиции записи в буфере могут быть разные, а не один эвент для всех точек. Обработки ошибок, как всегда, практически нет. основной модуль программы: На форме один комбобокс, в который заносятся названия устройств, один TEdit, в котором отображается имя wav-файла, SaveDialog+button для выбора файла, в который будет производиться запись, и один button для запуска/останова процесса записи. Ну и две текстовые метки. unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, directsound, StdCtrls, mmsystem, ExtCtrls, TeeProcs, TeEngine, Chart, Series,unit2; const wm_dserror=wm_user+1; type TForm1 = class(TForm) RecStopButton: TButton; devicesComboBox: TComboBox; Label2: TLabel; Label3: TLabel; FileNameEdit: TEdit; SaveDialog: TSaveDialog; BrowsButton: TButton; procedure RecStopButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure BrowsButtonClick(Sender: TObject); private procedure DSMessage(var Msg:TMessage);message wm_dserror; { Private declarations } public procedure startrecord; procedure stoprecord; { Public declarations } end; var Form1: TForm1; GUIDList: array of PGuid; fmt:twaveformatex; ds:TDSRecorder; implementation {$R *.DFM} function EnumCallback(lpGuid: PGUID; lpstrDescription: PChar; lpstrModule: PChar; lpContext: Pointer) : bool; stdcall; var devices:tcombobox; Begin // - добавляю в комбобокс название устройства DirectSound devices:=lpcontext; devices.Items.Add(string(lpstrDescription)); // - если есть указатель на GUID - запоминаю, для обращения к этому устройству // - Устройство по умолчанию не использует GUID, для него lpGUID=nil setlength(GUIDList,length(GUIDList)+1); if lpguid<>nil then Begin new(GUIDList[length(GUIDList)-1]); GUIDList[length(GUIDList)-1]^:=lpguid^; End; End; procedure TForm1.DSMessage(var Msg:TMessage); begin application.messagebox(pchar(dserrorstring(msg.WParam)),'Ошибка',mb_OK); end; procedure TForm1.startrecord; Begin ds:=TDSRecorder.Create(true); ds.HWNDOwner:=Handle; with fmt do Begin wFormatTag:=wave_format_pcm; cbSize:=0; nChannels:=1; nSamplesPerSec:=48000; wBitsPerSample:=16; nBlockAlign:=nChannels*wBitsPerSample shr 3; nAvgBytesPerSec:=nSamplesPerSec*nBlockAlign; end; ds.Format:=@fmt; ds.FileName:=FileNameEdit.Text; ds.Resume; end; procedure TForm1.stoprecord; Begin ds.Terminate; ds.WaitFor; ds.Free; ds:=nil; end; procedure TForm1.RecStopButtonClick(Sender: TObject); begin if not assigned(ds) then startrecord; else stoprecord; end; //****************************************************************************** procedure TForm1.FormCreate(Sender: TObject); var res:hresult; begin SaveDialog.InitialDir:=extractfilepath(application.ExeName); FileNameEdit.Text:=SaveDialog.InitialDir+'/Test.wav'; // - нахожу все устройства DirectSound // - название устройства заносится в комбобокс, а GUID, если имеется, в массив // - GUIDList res:=DirectSoundcaptureEnumerate(@EnumCallback, devicescombobox); // - если перебор устройств отработал с ошибкой - выход. // - Запись будет невозможна, так как кнопка записи неактивна. if res<>DS_OK then Begin dserrorstring(res); exit; End; // - Если есть хотя бы одно устройство DS имеется, устанавливаю в комбобоксе // - активным первый элемент списка и активизирую кнопку записи if devicescombobox.Items.Count>0 then Begin DevicesComboBox.ItemIndex:=0; RecStopButton.Enabled:=true; End; end; //****************************************************************************** procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var i:integer; begin if ds<>nil then stoprecord; // - Освобождаю массив GUIDList. if GUIDList<>nil then Begin for i:=0 to length(GUIDList)-1 do if GUIDList[i]<>nil then Begin dispose(GUIDList[i]); GUIDList[i]:=nil; End; finalize(GUIDList); End; end; procedure TForm1.BrowsButtonClick(Sender: TObject); begin if SaveDialog.Execute then FileNameEdit.Text:=SaveDialog.FileName; end; end. Поток, в котором собственно происходит запись: unit Unit2; interface uses Classes,DirectSound,mmsystem,windows; const // - кусками такого размера будет производиться запись в файл notifysize=8192; // - а это количество точек(позиций) уведомления в буфере, от этого параметра зависит // - размер буфера записи, он равен notifysize*notifycount notifycount=5; type TDSRecorder = class(TThread) private FResult :HResult; FGUID :PGUID; FFormat :PWaveFormatEx; FFileName :String; FDSCB :iDirectSoundCaptureBuffer; FDSC :IDirectSoundCapture; FDSCBDesc :TDSCBUFFERDESC; FHWNDOwner :THandle; FNotifyEvent :Thandle; FNotifies :array of TDSBPositionNotify; FDSNotify :IDirectSoundNotify; { Private declarations } protected procedure Execute; override; public property Format :PWaveFormatEx read FFormat write FFormat; property GUID :PGUID read FGUID write FGUID; property FileName :string read FFileName write FFileName; property HWNDOwner:THandle read FHWNDOwner write FHWNDOwner; end; implementation function mmiofourcc(ch1,ch2,ch3,ch4:char):fourcc; begin result:=byte(ch1) or byte(ch2)shl 8 or byte(ch3) shl 16 or byte(ch4)shl 24; end; { TDSRecorder } procedure TDSRecorder.Execute; var i :integer; FmmckinfoSubchunk :MMCKINFO; FmmckinfoParent :MMCKINFO; Fmmio :HMMIO; p1,p2 :pointer; size1,size2 :cardinal; FPosition :DWord; begin { Place thread code here } // - получаю ссылку на интерфейс DS FResult:=DirectSoundCaptureCreate(FGUID,FDSC,nil); if FResult<>DS_OK then Begin PostMessage(FHwndOwner,wm_dserror,FResult,0); exit; end; // - подготавливаю свойства и формат для буфера захвата with FDSCBDesc do Begin dwSize:=sizeof(TDSCBUFFERDESC); dwFlags:= 0; dwBufferBytes:=notifysize*notifycount; lpwfxFormat:=FFormat; dwFXCount:=0; dwReserved:=0; lpDSCFXDesc:=nil; End; // - получаю ссылку на интерфейс буфера захвата FResult:=FDSC.CreateCaptureBuffer(FDSCBDesc,FDSCB,nil); if FResult<>DS_OK then Begin PostMessage(FHwndOwner,wm_dserror,FResult,0); exit; end; // - подготавливаю массив позиций буфера захвата, при достижении которых буду получать уведомление. FNotifyEvent:=CreateEvent(nil,false,false,nil); setlength(FNotifies,notifycount); for i:=0 to length(FNotifies)-1 do Begin FNotifies[i].dwOffset:=notifysize*(i+1)-1; FNotifies[i].hEventNotify:=FNotifyEvent; end; // - установка позиций уведомления FDSCB.QueryInterface(IDirectSoundNotify,FDSNotify); if assigned(FDSNotify) then FDSNotify.SetNotificationPositions(length(FNotifies),@FNotifies[0]); // - создаю заголовок wave-файла Fmmio:=mmioopen(pchar(FFilename),nil,MMIO_CREATE or MMIO_WRITE); FmmckinfoParent.fccType:=mmiofourcc('W','A','V','E'); mmiocreatechunk(Fmmio,@FmmckinfoParent,MMIO_CREATERIFF); FmmckinfoSubchunk.ckid:=mmioFOURCC('f', 'm', 't', ' '); FmmckinfoSubchunk.fccType:=mmiofourcc('W','A','V','E'); mmiocreatechunk(Fmmio,@FmmckinfoSubchunk,0); mmiowrite( Fmmio,@FFormat^,sizeof(twaveformatex)); mmioascend(Fmmio,@FmmckinfoSubchunk,0); FmmckinfoSubchunk.ckid:=mmioFOURCC('d', 'a', 't', 'a'); mmiocreatechunk(Fmmio,@FmmckinfoSubchunk,0); // - запускаю буфер в режиме циклической записи FResult:=FDSCB.Start(DSBPLAY_LOOPING); if FResult<>DS_OK then PostMessage(FHwndOwner,wm_dserror,FResult,0); FPosition:=0; //*************************************************************************************** // - основной цикл. чтение производится кусками размером notifysize при достижении // - очередной точки уведомления, и size1 при вызове Lock будет возвращать это значение, // - а size2 всегда будет 0-м. Так было задумано. // - этот цикл может иметь много вариантов. Я его написал так, чтобы долго не мучаться. while not terminated do begin if waitforsingleobject(FNotifyEvent,500)=WAIT_TIMEOUT then continue; size1:=0;size2:=0; Fresult:=fdscb.Lock(Fposition,notifysize,p1,size1,p2,size2,0); FPosition:=FPosition+notifysize; // - Позиции уведосления принимают одни и те же значения: // - 0, notifysize, notifysize*2..., notifysize*notifycount(0), notifysize,.... // - Поэтому и позиция чтения Fposition принимает те же значения. // - Значение notifysize*notifycount соответствует позиции, следующей за концом буфера. // - Поскольку буфер циклический, реально это нулевая позиция. if fposition>=FDSCBDesc.dwBufferBytes then Fposition:=Fposition-FDSCBDesc.dwBufferBytes; if FResult=DS_OK then Begin // - Записываю очередную порцию данных в файл if size1>0 then mmiowrite( Fmmio,p1,size1); //if size2>0 then mmiowrite( Fmmio,p2,size2); fdscb.Unlock(p1,size1,p2,size2); end; end; //*************************************************************************************** // - закрываю все блоки wave-файла, и сам файл, при этом происходит корректировка размеров блоков(сhunk-ов) mmioascend(Fmmio,@FmmckinfoSubchunk,0); mmioascend(Fmmio,@FmmckinfoParent,0); mmioclose(Fmmio,0); // - останавливаю запись и освобождаю ресурсы FResult:=FDSCB.Stop; FDSNotify:=nil; finalize(FNotifies); CloseHandle(FNotifyEvent); end; end. |
Сообщ.
#8
,
|
|
|
не понял, а как передается в записывающий код номер или имя устройства с которого надо делать запись? например после выбора его в комбобоксе
Добавлено Дописал ds.GUID:=GUIDList[cbbComboBox.ItemIndex]; Но все равно ничего не пишет в файл с микрофона. Fresult:=fdscb.Lock(Fposition,notifysize,p1,size1,p2,size2,0); FPosition:=FPosition+notifysize; if fposition>=FDSCBDesc.dwBufferBytes then Fposition:=Fposition-FDSCBDesc.dwBufferBytes; if FResult=DS_OK then Begin здесь не попадает в запись в файл, потому что результат не DS_OK Добавлено Вываливается с кодом The parameter is incorrect |
Сообщ.
#9
,
|
|
|
Давно это было. Выкладывайте проект.
|
Сообщ.
#10
,
|
|
|
Вот такой код заработал
p1,p2 :PPointer;//pointer; size1,size2 :PDWORD;//cardinal; ... size1:=nil;size2:=nil; Fresult:=fdscb.Lock(Fposition,notifysize,@p1,@size1,@p2,@size2,0); FPosition:=FPosition+notifysize; if fposition>=FDSCBDesc.dwBufferBytes then Fposition:=Fposition-FDSCBDesc.dwBufferBytes; if FResult=DS_OK then Begin // - Записываю очередную порцию данных в файл if size1<>nil then begin mmiowrite(Fmmio,p1^,Integer(size1)); end; Fresult:=fdscb.Unlock(p1,Cardinal(size1),p2,Cardinal(size2)); ... |
Сообщ.
#11
,
|
|
|
Ужас. Выкладывайте проект.
|
Сообщ.
#12
,
|
|
|
Манера выкладывать не рабочий код это конечно жесть
Добавлено Зачем выкладывать? Изменения я написал. Это на ДельфиXe2. ДиректХ хедеры в ней включены кстати. |
Сообщ.
#13
,
|
|
|
Цитата Манера выкладывать не рабочий код это конечно жесть Если вы про мой код, то он рабочий. Ваш теперь уже как бы и не нужен - теперь нужны прототипы функций из хидеров, вероятно, они отличаются от тех, что я некогда использовал для D7. Указатель на указатель и адрес от указателя на указатель - это ж ужас. Адрес указателя на dword тоже. Нужно разбираться с типами данных. В смысле, разбирайтесь с типами данных. |
Сообщ.
#14
,
|
|
|
В частности
unit Winapi.DirectSound; теперь такая function Lock(dwOffset, dwBytes: DWORD; ppvAudioPtr1: PPointer; pdwAudioBytes1: PDWORD; ppvAudioPtr2: PPointer; pdwAudioBytes2: PDWORD; dwFlags: DWORD): HResult; stdcall; |
Сообщ.
#15
,
|
|
|
Тогда
p1,p2 :Pointer;//pointer; size1,size2 :DWORD;//cardinal; Fresult:=fdscb.Lock(Fposition,notifysize,@p1,@size1,@p2,@size2,0); "Мой" прототип выглядит(ел) так: Lock(dwWriteCursor, dwWriteBytes: DWORD; var lplpvAudioPtr1: Pointer; var lpdwAudioBytes1: DWORD; var lplpvAudioPtr2: Pointer; var lpdwAudioBytes2: DWORD; dwFlags: DWORD) : HResult; stdcall; |