Ir al contenido


Foto

CDDA Extractor (ripeador de audio SIN librerías)


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

#1 cHackAll

cHackAll

    Advanced Member

  • Administrador
  • 599 mensajes

Escrito 06 noviembre 2009 - 08:22

El siguiente ejemplo lo escribí con el fin de demostrar(me) que los programas que ripean audio (como el Easy CD-DA Extractor), no realizan ninguna cosa de otro mundo. También creo que es importante tener una herramienta que podamos personalizar, antes que la tecnología CDDA desaparezca.

El código mostrado a continuación muestra los pasos básicos para extraer la información de un CD de Audio, convirtiéndolo al formato mp3 sin el uso de librerías de terceros (bajadas de Internet).

En lugar de arrastrar librerías (Ej. LAME), que habrían de estar coladas en nuestros créditos, utiliza los mismos CODECS del S.O. utilizando su servicio ACM (Audio Compression Manager), produciendo MP3 con solo ~100 líneas de código.



delphi
  1. program ripCDDA; // by cHackAll
  2.  
  3. uses Windows, MMSystem;
  4.  
  5. const
  6. Drive = 'H'; //
  7. Sectors = 446; // ~1 Mb.
  8.  
  9. type
  10. TAcmStreamHeader = packed record
  11.   cbStruct, fdwStatus, dwUser: Cardinal;
  12.   pbSrc: PByte; cbSrcLength, cbSrcLengthUsed, dwSrcUser: Cardinal;
  13.   pbDst: PByte; cbDstLength, cbDstLengthUsed, dwDstUser: Cardinal;
  14.   dwReservedDriver: array [0..9] of Cardinal;
  15. end;
  16.  
  17. function acmStreamOpen(var phas: Cardinal; had: Cardinal; pwfxSrc, pwfxDst: PWaveFormatEx; pwfltr: Pointer; dwCallback, dwInstance, fdwOpen: Cardinal): Cardinal; stdcall external 'msacm32';
  18. function acmStreamSize(has, cbInput: Cardinal; var pdwOutputBytes: Cardinal; fdwSize: Cardinal): Cardinal; stdcall external 'msacm32';
  19. function acmStreamPrepareHeader(has: Cardinal; var pash: TAcmStreamHeader; fdwPrepare: Cardinal): Cardinal; stdcall external 'msacm32';
  20. function acmStreamConvert(has: Cardinal; var pash: TAcmStreamHeader; fdwConvert: Cardinal): Cardinal; stdcall external 'msacm32';
  21. function _itoa(Value: Integer; lpBuffer: PChar; Radix: Integer): PChar; cdecl external 'ntdll';
  22.  
  23. var
  24. TOC: record
  25.   Length: Word;
  26.   FirstTrack, LastTrack: Byte;
  27.   TrackData: array [1..100] of packed record
  28.   Reserved, ControlAdr, TrackNumber, Reserved1: Byte;
  29.   Address: packed record
  30.     case Integer of
  31.       0: (Value: Cardinal);
  32.       1: (Bytes: array [0..3] of Byte);
  33.   end;
  34.   end;
  35. end;
  36.  
  37. Info: record
  38.   DiskOffset: Int64;
  39.   SectorCount, TrackMode: Cardinal;
  40. end = (SectorCount: Sectors; TrackMode: 2{CDDA});
  41.  
  42. Buffer: array [1..2352{RAW_SECTOR_SIZE} * Sectors] of Byte;
  43. hDevice, Dummy, Index, Count, hFile, hStream: Cardinal;
  44. Header: TAcmStreamHeader = (cbStruct: SizeOf(Header); pbSrc: @Buffer);
  45. src: TWaveFormatEx = (wFormatTag: WAVE_FORMAT_PCM; nChannels: 2{Stereo}; nSamplesPerSec: 44100{44.1 kHz}; nAvgBytesPerSec: 176400; nBlockAlign: 4; wBitsPerSample: 16);
  46. dst: record
  47.   wfx: TWaveFormatEx;
  48.   wID: Word;
  49.   fdwFlags: Cardinal;
  50.   nBlockSize, nFramesPerBlock, nCodecDelay: Word;
  51. end = (wfx: (wFormatTag: 85{WAVE_FORMAT_MPEGLAYER3}; nChannels: 2; nSamplesPerSec: 44100; nAvgBytesPerSec: 24000; nBlockAlign: 1; cbSize: 12);
  52.         wID: 1{MPEGLAYER3_ID_MPEG}; fdwFlags: 2{MPEGLAYER3_FLAG_PADDING_OFF}; nBlockSize: 626; nFramesPerBlock: 1); { TODO : acm+VBR ? }
  53.  
  54. begin
  55. if GetDriveType(Drive + &#39;:&#39;) <> DRIVE_CDROM then Exit;
  56. hDevice := _lopen(&#39;\\.\&#39; + Drive + &#39;:&#39;, OF_READ);
  57. if not DeviceIoControl(hDevice, $2D4800{IOCTL_STORAGE_CHECK_VERIFY}, nil, 0, nil, 0, Dummy, nil) then
  58.   DeviceIoControl(hDevice, $2D4808{IOCTL_STORAGE_EJECT_MEDIA}, nil, 0, nil, 0, Dummy, nil)
  59. else if DeviceIoControl(hDevice, $90018{FSCTL_LOCK_VOLUME}, nil, 0, nil, 0, Dummy, nil) then
  60.   begin
  61.   Dummy := 1{True};
  62.   DeviceIoControl(hDevice, $2D4804{IOCTL_STORAGE_MEDIA_REMOVAL}, @Dummy, 1, nil, 0, Dummy, nil);
  63.   DeviceIoControl(hDevice, $24000{IOCTL_CDROM_READ_TOC}, nil, 0, @TOC, SizeOf(TOC), Dummy, nil);
  64.  
  65.   for Index := TOC.FirstTrack to TOC.LastTrack + 1 do
  66.     with TOC.TrackData[Index] do
  67.     Address.Value := (Address.Bytes[1] * 4500 + Address.Bytes[2] * 75 + Address.Bytes[3]) - 150;
  68.  
  69.   acmStreamOpen(hStream, 0, @src, @dst, nil, 0, 0, 4{ACM_STREAMOPENF_NONREALTIME});
  70.   acmStreamSize(hStream, SizeOf(Buffer), Header.cbDstLength, 0{ACM_STREAMSIZEF_SOURCE});
  71.   Header.pbDst := Ptr(LocalAlloc(0, Header.cbDstLength));
  72.  
  73.   for Index := TOC.FirstTrack to TOC.LastTrack do
  74.     with TOC.TrackData[Index] do
  75.     if ControlAdr = $12 then // See specification T10/1363-D Revision-02A, by [url]http://www.incits.org/[/url]
  76.       begin
  77.       Info.DiskOffset := Address.Value * 2048{COOKED_SECTOR_SIZE};
  78.       Count := TOC.TrackData[Index + 1].Address.Value - Address.Value;
  79.  
  80.       _itoa(Index, @lstrcpy(@Buffer, &#39;Track&#39;)[6], 10);
  81.       hFile := _lcreat(lstrcat(@Buffer, &#39;.mp3&#39;), 0);
  82.       repeat
  83.         if Count < Sectors then
  84.         Info.SectorCount := Count;
  85.  
  86.         DeviceIoControl(hDevice, $2403E{IOCTL_CDROM_RAW_READ}, @Info, SizeOf(Info), @Buffer, SizeOf(Buffer), Header.cbSrcLength, nil);
  87.         acmStreamPrepareHeader(hStream, Header, 0);
  88.         acmStreamConvert(hStream, Header, 4{ACM_STREAMCONVERTF_BLOCKALIGN});
  89.         _lwrite(hFile, PChar(Header.pbDst), Header.cbDstLengthUsed);
  90.  
  91.         Inc(Info.DiskOffset, 2048{COOKED_SECTOR_SIZE} * Info.SectorCount);
  92.         Dec(Count, Info.SectorCount);
  93.       until Count = 0;
  94.       end;
  95.  
  96.     Dummy := 0;
  97.     DeviceIoControl(hDevice, $2D4804{IOCTL_STORAGE_MEDIA_REMOVAL}, @Dummy, 1, nil, 0, Dummy, nil);
  98.   end;
  99. end { TODO : MemoryLeak } .



Enjoy!
  • 0

#2 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 06 noviembre 2009 - 09:34

:o no mames, esto está genial !!! :o le hago reverencias compañero :).
  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 06 noviembre 2009 - 10:02

Indudablemente, querido little bro, es impresionante el uso que haces del API de Windows y sobre todo la costumbre que tienes de hacer código en pocos bytes.

Excelente aporte, muchas gracias. (y)

Salud OS
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 06 noviembre 2009 - 06:36

Excelente, amigo (y). Cuando disponga de un poco mas de tiempo lo estudiaré con detenimiento. Es de esos temas que no se pueden dejas escapar. :D

Saludos.
  • 0

#5 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 06 noviembre 2009 - 06:56

Hola
Bueno yo solo remarco. :D

[move]El siguiente ejemplo lo escribí con el fin de demostrar(me) que ........... con solo ~100 líneas de código.[/move]

El siguiente código es para re contra demostrarnos que tenemos un amigo maestro.
Wow amigo, siempre haciendo cosas impresionantes.
Saludos

  • 0

#6 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 06 noviembre 2009 - 07:50

Impresionante aporte..... gracias amigo
  • 0

#7 FGarcia

FGarcia

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 687 mensajes
  • LocationMéxico

Escrito 16 noviembre 2009 - 12:31

¡Carajo!

A los 25 despues del trabajo todo eran cantinas, bules y pura diversion, la escuela habia quedado atras!

¿Que sera de este "chiquillo" cuando tenga nuestra edad? (Eliseo, Carlos, Joseme y demas del rango)

Si la reencarnacion es cierta quiero regresar asi mas o menos como un coktel cHackAll / seoane!!

Ando algo desconectado por causas de trabajo pero los ando vigilando... :p

Saludos!!!
  • 0

#8 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4.483 mensajes
  • LocationVenezuela

Escrito 16 noviembre 2009 - 07:22

Hola, aqui vengo yo con mi ignorancia, tengo rato dando vueltas y pensando como hacer para que funcione y aun no doy con el truco.  Que tengo que hacer  :lipsrsealed:
  • 0

#9 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 16 noviembre 2009 - 08:12

Agarré un momento para probarlo, y pues al igual que eduardo no he podido hacer nada :s
  • 0

#10 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 16 noviembre 2009 - 08:46



delphi
  1. program ripCDDA; // by cHackAll
  2.  
  3. uses Windows, MMSystem;
  4.  
  5. const
  6. Drive = 'H'; //
  7. Sectors = 446; // ~1 Mb.
  8. .......



Sustituir 'H' por la unidad de CD

Saludos.

  • 0

#11 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 16 noviembre 2009 - 08:51



delphi
  1. program ripCDDA; // by cHackAll
  2.  
  3. uses Windows, MMSystem;
  4.  
  5. const
  6. Drive = 'H'; //
  7. Sectors = 446; // ~1 Mb.
  8. .......



Sustituir 'H' por la unidad de CD

Saludos.


Hasta aquí yo lo entiendo, pero ¿donde se guarda?, ¿como ejecutarlo?, ¿donde lo podemos ver?.
  • 0

#12 cHackAll

cHackAll

    Advanced Member

  • Administrador
  • 599 mensajes

Escrito 16 noviembre 2009 - 08:55

...¿donde se guarda?...


En la misma carpeta donde este el ejecutable.

...¿como ejecutarlo?...


Doble click al ejecutable o F9 usando el IDE de Delphi

...¿donde lo podemos ver?...


?
  • 0

#13 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 16 noviembre 2009 - 09:11

Nada de Nada amigo.
  • 0

#14 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4.483 mensajes
  • LocationVenezuela

Escrito 16 noviembre 2009 - 09:19

a ver, estoy haciendolo en ventanitas  :wink:, pero no me furula, lo compilo y no me da errores, luego meto un CD de audio en la unidad.  Lo corro paso a paso pero no entra en ninguna de las dos condicionales:



delphi
  1. if not DeviceIoControl(hDevice, $2D4800{IOCTL_STORAGE_CHECK_VERIFY}, nil, 0, nil, 0, Dummy, nil) then
  2.   DeviceIoControl(hDevice, $2D4808{IOCTL_STORAGE_EJECT_MEDIA}, nil, 0, nil, 0, Dummy, nil)
  3. else if DeviceIoControl(hDevice, $90018{FSCTL_LOCK_VOLUME}, nil, 0, nil, 0, Dummy, nil) then
  4.   begin

Archivos adjuntos


  • 0

#15 cHackAll

cHackAll

    Advanced Member

  • Administrador
  • 599 mensajes

Escrito 16 noviembre 2009 - 10:16

Nada de Nada amigo.


Revisando el comportamiento del código en el equipo de enecumene pude encontrar 3 problemas;
  • en la Linea 75 hay una validación, que debería haber sido "exacta" para evitar tener problemas en discos multisesión, al parecer dicho valor es mas variable de lo esperado y sin las especificaciones no quedara mas que seguir analizándolo ($12 -> $10)
  • En la linea 80 se esta realizando una concatenación con un índice erróneo ([6] -> [5])

Pero el mayor problema parece estar relacionado con los CODECS del S.O., haré un par de pruebas en ambientes limpios y vuelvo a publicar el código.
  • 0

#16 cHackAll

cHackAll

    Advanced Member

  • Administrador
  • 599 mensajes

Escrito 17 noviembre 2009 - 08:40



delphi
  1. program ripCDDA; // by cHackAll
  2.  
  3. uses Windows, MMSystem;
  4.  
  5. const
  6. Drive = 'Z'; //
  7. Sectors = 446; // ~1 Mb.
  8.  
  9. type
  10. TAcmStreamHeader = packed record
  11.   cbStruct, fdwStatus, dwUser: Cardinal;
  12.   pbSrc: PByte; cbSrcLength, cbSrcLengthUsed, dwSrcUser: Cardinal;
  13.   pbDst: PByte; cbDstLength, cbDstLengthUsed, dwDstUser: Cardinal;
  14.   dwReservedDriver: array [0..9] of Cardinal;
  15. end;
  16.  
  17. function acmStreamOpen(var phas: Cardinal; had: Cardinal; pwfxSrc, pwfxDst: PWaveFormatEx; pwfltr: Pointer; dwCallback, dwInstance, fdwOpen: Cardinal): Cardinal; stdcall external 'msacm32';
  18. function acmFormatSuggest(had: Cardinal; pwfxSrc, pwfxDst: PWaveFormatEx; cbwfxDst, fdwSuggest: Cardinal): Cardinal; stdcall external 'msacm32';
  19. function acmStreamSize(has, cbInput: Cardinal; var pdwOutputBytes: Cardinal; fdwSize: Cardinal): Cardinal; stdcall external 'msacm32';
  20. function acmStreamPrepareHeader(has: Cardinal; var pash: TAcmStreamHeader; fdwPrepare: Cardinal): Cardinal; stdcall external 'msacm32';
  21. function acmStreamConvert(has: Cardinal; var pash: TAcmStreamHeader; fdwConvert: Cardinal): Cardinal; stdcall external 'msacm32';
  22. function _itoa(Value: Integer; lpBuffer: PChar; Radix: Integer): PChar; cdecl external 'ntdll';
  23.  
  24. var
  25. TOC: record
  26.   Length: Word;
  27.   FirstTrack, LastTrack: Byte;
  28.   TrackData: array [1..100] of packed record
  29.   Reserved, ControlAdr, TrackNumber, Reserved1: Byte;
  30.   Address: packed record
  31.     case Integer of
  32.       0: (Value: Cardinal);
  33.       1: (Bytes: array [0..3] of Byte);
  34.   end;
  35.   end;
  36. end;
  37.  
  38. Info: record
  39.   DiskOffset: Int64;
  40.   SectorCount, TrackMode: Cardinal;
  41. end = (SectorCount: Sectors; TrackMode: 2{CDDA});
  42.  
  43. Buffer: array [1..2352{RAW_SECTOR_SIZE} * Sectors] of Byte;
  44. hDevice, Dummy, Index, Count, hFile, hStream: Cardinal;
  45. Header: TAcmStreamHeader = (cbStruct: SizeOf(Header); pbSrc: @Buffer);
  46. src: TWaveFormatEx = (wFormatTag: WAVE_FORMAT_PCM; nChannels: 2{Stereo}; nSamplesPerSec: 44100{44.1 kHz}; nAvgBytesPerSec: 176400; nBlockAlign: 4; wBitsPerSample: 16);
  47. dst: record
  48.   wfx: TWaveFormatEx;
  49.   wID: Word;
  50.   fdwFlags: Cardinal;
  51.   nBlockSize, nFramesPerBlock, nCodecDelay: Word;
  52. end = (wfx: (wFormatTag: 85{WAVE_FORMAT_MPEGLAYER3}; nChannels: 2; nSamplesPerSec: 44100; nAvgBytesPerSec: 24000; nBlockAlign: 1));
  53.  
  54. begin
  55. Set8087CW($133F); // +I+Z+O-S
  56. if GetDriveType(Drive + ':') <> DRIVE_CDROM then Exit;
  57. hDevice := _lopen('\\.\' + Drive + ':', OF_READ);
  58. if not DeviceIoControl(hDevice, $2D4800{IOCTL_STORAGE_CHECK_VERIFY}, nil, 0, nil, 0, Dummy, nil) then
  59.   DeviceIoControl(hDevice, $2D4808{IOCTL_STORAGE_EJECT_MEDIA}, nil, 0, nil, 0, Dummy, nil)
  60. else if DeviceIoControl(hDevice, $90018{FSCTL_LOCK_VOLUME}, nil, 0, nil, 0, Dummy, nil) then
  61.   begin
  62.   Dummy := 1{True};
  63.   DeviceIoControl(hDevice, $2D4804{IOCTL_STORAGE_MEDIA_REMOVAL}, @Dummy, 1, nil, 0, Dummy, nil);
  64.   DeviceIoControl(hDevice, $24000{IOCTL_CDROM_READ_TOC}, nil, 0, @TOC, SizeOf(TOC), Dummy, nil);
  65.  
  66.   for Index := TOC.FirstTrack to TOC.LastTrack + 1 do
  67.     with TOC.TrackData[Index] do
  68.     Address.Value := (Address.Bytes[1] * 4500 + Address.Bytes[2] * 75 + Address.Bytes[3]) - 150;
  69.  
  70.   if acmStreamOpen(hStream, 0, @src, @dst, nil, 0, 0, 4{ACM_STREAMOPENF_NONREALTIME}) <> 0 then
  71.     begin
  72.     acmFormatSuggest(0, @src, @dst, SizeOf(dst), $10000{ACM_FORMATSUGGESTF_WFORMATTAG});
  73.     acmStreamOpen(hStream, 0, @src, @dst, nil, 0, 0, 4{ACM_STREAMOPENF_NONREALTIME});
  74.     end;
  75.   acmStreamSize(hStream, SizeOf(Buffer), Header.cbDstLength, 0{ACM_STREAMSIZEF_SOURCE});
  76.   Header.cbDstLength := Header.cbDstLength * 2;
  77.   Header.pbDst := Ptr(LocalAlloc(0, Header.cbDstLength));
  78.  
  79.   for Index := TOC.FirstTrack to TOC.LastTrack do
  80.     with TOC.TrackData[Index] do { TODO : Audio CD ? }
  81. //    if ControlAdr = $12 then
  82.       begin
  83.       Info.DiskOffset := Address.Value * 2048{COOKED_SECTOR_SIZE};
  84.       Count := TOC.TrackData[Index + 1].Address.Value - Address.Value;
  85.  
  86.       _itoa(Index, @lstrcpy(@Buffer, 'Track')[5], 10);
  87.       hFile := _lcreat(lstrcat(@Buffer, '.mp3'), 0);
  88.       repeat
  89.         if Count < Sectors then
  90.         Info.SectorCount := Count;
  91.  
  92.         DeviceIoControl(hDevice, $2403E{IOCTL_CDROM_RAW_READ}, @Info, SizeOf(Info), @Buffer, SizeOf(Buffer), Header.cbSrcLength, nil);
  93.         acmStreamPrepareHeader(hStream, Header, 0);
  94.  
  95.         acmStreamConvert(hStream, Header, 4{ACM_STREAMCONVERTF_BLOCKALIGN});
  96.         _lwrite(hFile, PChar(Header.pbDst), Header.cbDstLengthUsed);
  97.  
  98.         Inc(Info.DiskOffset, 2048{COOKED_SECTOR_SIZE} * Info.SectorCount);
  99.         Dec(Count, Info.SectorCount);
  100.       until Count = 0;
  101.       end;
  102.  
  103.     Dummy := 0;
  104.     DeviceIoControl(hDevice, $2D4804{IOCTL_STORAGE_MEDIA_REMOVAL}, @Dummy, 1, nil, 0, Dummy, nil);
  105.   end;
  106. // Set8087CW(Saved8087CW);
  107. end { TODO : MemoryLeak } .


  • 0

#17 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 17 noviembre 2009 - 09:15

Lo acabo de probar y ahora sí funciona ;) (y)
  • 0




IP.Board spam blocked by CleanTalk.