Ir al contenido



Foto

Capturar y guardar sonido en formato mp3.


  • Por favor identifícate para responder
No hay respuestas en este tema

#1 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.857 mensajes
  • LocationMadrid - España

Escrito 07 enero 2017 - 02:58

Tras publicar Capturar y guardar sonido en formato wav os presento una nueva aplicación, basada en la anterior, que captura audio en mp3. Esta basado en el encoder de Lame y su lame_enc.dll.

 

He preparado una unit de captura (uMP3Capture) que se puede usar con una app clásica VCL. uMP3Capture usa un thread de captura y se comunica por mensajes con la App que lo usa.


delphi
  1. unit uMP3Capture;
  2.  
  3. interface
  4.  
  5. uses
  6. Windows, Messages, MMSystem;
  7.  
  8.  
  9. const
  10. THREAD_QUERY_INFORMATION = 64;
  11. UM_FINISH = WM_USER + 1;
  12.  
  13. type
  14. PSHORT = ^SHORT;
  15. SHORT = Smallint;
  16.  
  17. // Modos Lame VBR (lame_enc.dll)
  18. Tvbr_mode = (
  19. vbr_off = 0,
  20. vbr_mt, // obsolete, same as vbr_mtrh
  21. vbr_rh,
  22. vbr_abr,
  23. vbr_mtrh,
  24. vbr_max_indicator, // Don't use this! It's used for sanity checks.
  25. vbr_default = vbr_mtrh // change this to change the default VBR mode of LAME
  26. );
  27.  
  28. // Estructura que se para a la rutina CallBack de captura WaveInProc
  29. PWAVEIN_DATA = ^WAVEIN_DATA;
  30. WAVEIN_DATA = record
  31. hFile: THANDLE;
  32. Lame: Pointer;
  33. BufferMp3: PBYTE;
  34. Finish: BOOL;
  35. end;
  36.  
  37. // Parámetros pasados a el hilo (thread) de captura
  38. PTH_PARAM_WAVCAPTURE = ^TH_PARAM_WAVCAPTURE;
  39. TH_PARAM_WAVCAPTURE = record
  40. Wnd: HWND;
  41. FileName: array [0..MAX_PATH] of CHAR;
  42. end;
  43.  
  44. function wsprintf(lpOut, lpFmt: PChar): Integer; cdecl; varargs; external 'User32.dll' name 'wsprintfA';
  45. function OpenThread(DesiredAccess: DWORD; bInheritHandle: BOOL; dwThreadId: DWORD): THANDLE; stdcall; external 'Kernel32';
  46. function GetMP3FileName(Buffer: PCHAR): PCHAR;
  47. function MP3Capture(Wnd: HWND; FileName: PCHAR): BOOL;
  48. function StopMP3Capture: DWORD;
  49.  
  50. // Funciones usadas de lame_enc.dll
  51. function lame_init(): Pointer; cdecl; external 'lame_enc';
  52. function lame_set_in_samplerate(lgf: Pointer; SampleRate: integer): integer; cdecl; external 'lame_enc';
  53. function lame_set_VBR(lgf: Pointer; mode: Tvbr_mode): integer; cdecl; external 'lame_enc';
  54. function lame_init_params(lgf: Pointer): integer; cdecl; external 'lame_enc';
  55. function lame_encode_flush(lgf: Pointer; mp3buf: PBYTE; size: integer): integer; cdecl; external 'lame_enc';
  56. function lame_encode_buffer_interleaved(lgf: Pointer; pcm: PSHORT; num_samples: integer; mp3buf: PBYTE; mp3buf_size: integer): integer; cdecl; external 'lame_enc';
  57. function lame_close(lgf: Pointer): integer; cdecl; external 'lame_enc';
  58.  
  59.  
  60. implementation
  61.  
  62. var
  63. ThreadId: DWORD = 0;
  64. Param: TH_PARAM_WAVCAPTURE;
  65.  
  66.  
  67.  
  68. function GetMP3FileName(Buffer: PCHAR): PCHAR;
  69. var
  70. Now: SYSTEMTIME;
  71. begin
  72. GetLocalTime(Now);
  73. wsprintf(Buffer, '%d%d%d%d%d.mp3', Now.wYear, Now.wMonth, Now.wDay, Now.wHour, Now.wSecond);
  74. Result:= Buffer;
  75. end;
  76.  
  77. function KeyboardEnd: BOOL;
  78. begin
  79. Result:= BOOL(GetAsyncKeyState(VK_ESCAPE));
  80. end;
  81.  
  82. // Rutina CallBack de captura
  83. procedure WaveInProc(WaveIn: HWAVEIN; uMsg: UINT; Data: PWAVEIN_DATA; WaveHdr: PWAVEHDR; dwParam2: DWORD); stdcall;
  84. var
  85. MP3Writed: integer;
  86. begin
  87. if uMsg = WIM_DATA then
  88. begin
  89. // Compresión MP3
  90. if WaveHdr.dwBytesRecorded = 0 then
  91. MP3Writed:= Lame_encode_flush(Data.Lame, Data.BufferMp3, WaveHdr.dwBufferLength div 4)
  92. else
  93. MP3Writed:= Lame_encode_buffer_interleaved(Data.Lame, PSHORT(WaveHdr.lpData), WaveHdr.dwBytesRecorded div 4, Data.BufferMp3, WaveHdr.dwBufferLength div 4);
  94.  
  95. // Escritura del buffer
  96. WriteFile(Data.hFile, Data.BufferMp3^, MP3Writed, PDWORD(0)^, nil);
  97.  
  98. // Siguiente Captura
  99. if not Data.Finish then
  100. waveInAddBuffer(WaveIn, WaveHdr, sizeof(TWAVEHDR));
  101. end;
  102. end;
  103.  
  104. function AudioCapture(FileName: PCHAR): BOOL;
  105. var
  106. WaveFormatEx: TWAVEFORMATEX;
  107. WaveIn: HWAVEIN;
  108. WaveHdr: array [0..1] of TWAVEHDR;
  109. Data: WAVEIN_DATA;
  110. hFile: THANDLE;
  111. msg: TMsg;
  112. i: integer;
  113. Lame: Pointer;
  114. begin
  115. Result:= FALSE;
  116. WaveFormatEx.wFormatTag:= WAVE_FORMAT_PCM; // simple, uncompressed format
  117. WaveFormatEx.nChannels:= 2; // 1 = mono, 2 = stereo
  118. WaveFormatEx.nSamplesPerSec:= 44100; // sampleRate
  119. WaveFormatEx.wBitsPerSample:= 16; // 16 for high quality, 8 for telephone-grade
  120. WaveFormatEx.nBlockAlign:= WaveFormatEx.nChannels * (WaveFormatEx.wBitsPerSample div 8); //nChannels * (wBitsPerSample/8)
  121. WaveFormatEx.nAvgBytesPerSec:= WaveFormatEx.nSamplesPerSec * WaveFormatEx.nBlockAlign;
  122. WaveFormatEx.cbSize:= 0;
  123.  
  124. hFile:= CreateFile(FileName, GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  125. if hFile <> INVALID_HANDLE_VALUE then
  126. begin
  127. Lame:= Lame_init;
  128. Lame_set_in_samplerate(Lame, WaveFormatEx.nSamplesPerSec); //44100
  129. Lame_set_VBR(Lame, vbr_default);
  130. Lame_init_params(Lame);
  131.  
  132. Data.BufferMp3:= Ptr(LocalAlloc(0, WaveFormatEx.nAvgBytesPerSec div 4));
  133. Data.Lame:= Lame;
  134. Data.hFile:= hFile;
  135. Data.Finish:= FALSE;
  136.  
  137. if waveInOpen(@WaveIn, WAVE_MAPPER, @WaveFormatEx, DWORD(@WaveInProc), DWORD(@Data), CALLBACK_FUNCTION) = MMSYSERR_NOERROR then
  138. begin
  139. for i:=0 to 1 do
  140. begin
  141. WaveHdr[i].dwBufferLength:= WaveFormatEx.nAvgBytesPerSec;
  142. WaveHdr[i].lpData:= Ptr(LocalAlloc(0, WaveHdr[i].dwBufferLength));
  143. WaveHdr[i].dwFlags:= 0;
  144. WaveHdr[i].dwBytesRecorded:= 0;
  145. waveInPrepareHeader(WaveIn, @WaveHdr[i], sizeof(WAVEHDR));
  146. waveInAddBuffer(WaveIn, @WaveHdr[i], sizeof(WAVEHDR));
  147. end;
  148. waveInStart(WaveIn);
  149. Result:= TRUE;
  150.  
  151. // Bucle de captura.
  152. repeat
  153. PeekMessage(msg,0,0,0, PM_REMOVE);
  154. Data.Finish:= KeyboardEnd();
  155. Sleep(100);
  156. until (msg.message = WM_QUIT) or Data.Finish;
  157.  
  158.       // Cerrando waveIn antes que Lame
  159.       if WaveIn <> 0 then
  160.       begin
  161.         Data.Finish:= TRUE;
  162.         waveInStop(WaveIn);
  163.         waveInReset(WaveIn);
  164.         waveInClose(WaveIn);
  165.         for i:=0 to 1 do
  166.           LocalFree(DWORD(WaveHdr[i].lpData));
  167.       end;
  168.       // Cerrando Lame tras WaveIn para que pueda hacer flush
  169.       Lame_close(Lame);
  170.       LocalFree(DWORD(Data.BufferMp3));
  171. end;
  172. CloseHandle(hFile);
  173. end;
  174. end;
  175.  
  176. // Thread de captura
  177. function thCapture(var P: TH_PARAM_WAVCAPTURE): BOOL; stdcall;
  178. begin
  179. DeleteFile(P.FileName);
  180.  
  181. PostMessage(P.Wnd, UM_FINISH, 1, 0);
  182. Result:= AudioCapture(P.FileName);
  183. PostMessage(P.Wnd, UM_FINISH, 0, DWORD(Result));
  184. ThreadId:= 0;
  185. end;
  186.  
  187. function MP3Capture(Wnd: HWND; FileName: PCHAR): BOOL;
  188. begin
  189. Result:= FALSE;
  190. if ThreadId = 0 then
  191. begin
  192. Param.Wnd:= Wnd;
  193. lstrcpy(Param.FileName, FileName);
  194. CloseHandle(CreateThread(nil, 0, @thCapture, @Param, 0, ThreadId));
  195. Result:= TRUE;
  196. end;
  197. end;
  198.  
  199. function StopMP3Capture: DWORD;
  200. var
  201. hThread: THANDLE;
  202. begin
  203. Result:= 0;
  204. if ThreadId <> 0 then
  205. begin
  206. hThread:= OpenThread(SYNCHRONIZE or THREAD_QUERY_INFORMATION, FALSE, ThreadId);
  207. PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
  208. WaitForSingleObject(hThread, INFINITE);
  209. GetExitCodeThread(hThread, Result);
  210. CloseHandle(hThread);
  211. ThreadId:= 0;
  212. end;
  213. end;
  214.  
  215. end.

Espero que sirva de ejemplo de como usar el excelente encoder de Lame, del que hago una pequeña muestra.

 

La app es completamente funcional, capturando el sonido de la tarjeta de sonido del PC siempre que esté configurada como dispositivo de grabación. Con  el uso de las funciones presentadas de la librería de lame_enc.dll, podemos escribir sin grandes problemas un convertidor de archivos Wave -> MP3. Seguro que le encontráis utilidad.

 

Subo binario y fuente.

 

 

Saludos.

 

 

.

Archivos adjuntos


  • 2