(******************************************************************************
 *                                                                            *
 *  Придумал и написал Кода Виктор, Март 2002                                 *
 *                                                                            *
 *  Файл:       lowfunc.pas                                                   *
 *  Содержание: Функции для низкоуровневого доступа к данным wav-файлов       *
 *                                                                            *
 ******************************************************************************)
unit lowfunc;

interface

uses
  Windows,
  MMSystem,
  DirectSound;

type
  PPWAVEFORMATEX = ^PWAVEFORMATEX; // Необходим новый тип (в SDK - WAVEFORMAT**)

// Комментируя флаги условной компиляции, можно размешать функции в exe-модуле
// или загружать их из динамической библиотеки

//{$DEFINE OPEN_EXTERNAL}
//{$DEFINE READ_MMIO_EXTERNAL}
//{$DEFINE START_EXTERNAL}
{$DEFINE WAVE_READ_EXTERNAL}   // Эта функция находится в DLL


function WaveOpenFile(
  strFileName: PChar;          // Имя открываемого файла (c расширением .wav)
  phmmioIn:    PHMMIO;
  ppwfxInfo:   PPWAVEFORMATEX;
  pckInRIFF:   PMMCKINFO
): HRESULT; {$IFDEF OPEN_EXTERNAL}
            stdcall; external 'wave.dll' name 'WaveOpenFile';
            {$ENDIF}

function ReadMMIO(
  hmmioIn:   HMMIO;
  pckInRIFF: PMMCKINFO;
  ppwfxInfo: PPWAVEFORMATEX
): HRESULT; {$IFDEF READ_MMIO_EXTERNAL}
            stdcall; external 'wave.dll' name 'ReadMMIO';
            {$ENDIF}

function WaveStartDataRead(
  phmmioIn:  PHMMIO;
  pckIn:     PMMCKINFO;
  pckInRIFF: PMMCKINFO
): HRESULT; {$IFDEF START_EXTERNAL}
            stdcall; external 'wave.dll' name 'WaveStartDataRead';
            {$ENDIF}

function WaveReadFile(
  hmmioIn:      HMMIO;
  cbRead:       UINT;
  pbDest:       PByte;
  pckIn:        PMMCKINFO;
  cbActualRead: PUINT
): HRESULT; {$IFDEF WAVE_READ_EXTERNAL}
            stdcall; external 'wave.dll' name 'WaveReadFile';
            {$ENDIF}

implementation

{$IFNDEF OPEN_EXTERNAL}

//------------------------------------------------------------------------------
// Имя:      WaveOpenFile()
// Описание: Открывает файл и подготавливает его для чтения
//------------------------------------------------------------------------------
function WaveOpenFile( strFileName: PChar; phmmioIn: PHMMIO;
                       ppwfxInfo: PPWAVEFORMATEX; pckInRIFF: PMMCKINFO ): HRESULT;
var
  hmmioIn: HMMIO;
begin
  Result := E_FAIL;

  hmmioIn := 0;

  hmmioIn := mmioOpen( strFileName, nil, MMIO_ALLOCBUF or MMIO_READ );

  if hmmioIn = 0 then // SDK hmmioIn == NULL
     Exit;

  if FAILED( ReadMMIO( hmmioIn, pckInRIFF, ppwfxInfo ) ) then
  begin
    mmioClose( hmmioIn, 0 );
    Exit;
  end;

  phmmioIn^ := hmmioIn;

  Result := S_OK;
end;
{$ENDIF}




{$IFNDEF READ_MMIO_EXTERNAL}
//------------------------------------------------------------------------------
// Имя:      ReadMMIO()
// Описание: Читает MMIO-данные из файла (НЕДОПИСАНА ДО КОНЦА, НО РАБОТАЕТ!)
//------------------------------------------------------------------------------
function ReadMMIO( hmmioIn: HMMIO; pckInRIFF: PMMCKINFO;
                   ppwfxInfo: PPWAVEFORMATEX ): HRESULT;
var
  ckIn:         MMCKINFO;              //?
  pcmWavFormat: PCMWAVEFORMAT;
  cbExtraBytes: WORD;
begin
  Result := E_FAIL;

  ppwfxInfo^ := nil;

  if mmioDescend( hmmioIn, pckInRIFF, nil, 0 ) <> 0 then
     Exit;

  if ( pckInRIFF.ckid <> FOURCC_RIFF ) or
     ( pckInRIFF.fccType <> mmioStringToFOURCC( 'wave', MMIO_TOUPPER ) ) then // SDK mmioFOURCC('W', 'A', 'V', 'E') - но её в mmsystem.pas нет
     Exit;

  ckIn.ckid := mmioStringToFOURCC( 'fmt ', 0 );
  if mmioDescend( hmmioIn, @ckIn, pckInRIFF, MMIO_FINDCHUNK ) <> 0 then
     Exit;

  // Вот так на С++
  // if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
  //     return E_FAIL;

  // А вот так тут:
  if ckIn.cksize < {DWORD?}SizeOf( PCMWAVEFORMAT ) then
     Exit;

  if mmioRead( hmmioIn, PChar( @pcmWavFormat ),
              SizeOf( PCMWAVEFORMAT ) ) <> SizeOf( PCMWAVEFORMAT ) then
     Exit;

  if pcmWavFormat.wf.wFormatTag = WAVE_FORMAT_PCM then
  begin
    GetMem( ppwfxInfo^, SizeOf( TWAVEFORMATEX ) );
    CopyMemory( ppwfxInfo^, @pcmWavFormat, SizeOf( PCMWAVEFORMAT ) );
    ppwfxInfo^.cbSize := 0;
  end
  else  // Формат wav-файла, отличный от WAVE_PCM_FORMAT
  begin // Например C:\Windows\Media\START.WAV
    // Тут недописано!
    MessageBox( 0, 'pcmWavFormat.wf.wFormatTag <> WAVE_FORMAT_PCM',
               'Пока НЕДОДЕЛАНО :-[ ]',
                MB_OK );
    Exit;

    cbExtraBytes := 0; // SDK - 0L

    if mmioRead( hmmioIn, PChar( @cbExtraBytes ), SizeOf( WORD ) ) <> SizeOf( WORD ) then
       Exit;

    // Вот так на С++
    // *ppwfxInfo = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ];
    // if( NULL == *ppwfxInfo )
    //     return E_FAIL;
    // А вот так пытался я (это даже не пропускает компилятор):
    // GetMem( ppwfxInfo^, PWAVEFORMATEX( Char (SizeOf( TWAVEFORMATEX )+ cbExtraBytes) ) ); //???

    if ppwfxInfo^ = nil then
       Exit;

    CopyMemory( ppwfxInfo^, @pcmWavFormat, SizeOf( PCMWAVEFORMAT ) );

    ppwfxInfo^.cbSize := cbExtraBytes;



    // Вот так в C++
    // if( mmioRead( hmmioIn, (CHAR*)(((BYTE*)&((*ppwfxInfo)->cbSize))+sizeof(WORD)),
    //               cbExtraBytes ) != cbExtraBytes )
    //    {
    //        delete *ppwfxInfo;
    //        *ppwfxInfo = NULL;
    //        return E_FAIL;
    //    }

    // А вот так тут:
    if mmioRead( hmmioIn, PChar( PByte( ppwfxInfo^.cbSize ) ) + SizeOf( WORD ),
                 cbExtraBytes) <> cbExtraBytes then
    begin
      Dispose( ppwfxInfo^ ); // А не FreeMem() ???
      ppwfxInfo^ := nil;
      Result := E_FAIL;
      Exit;
    end;
  end;

  // Ascend
  if mmioAscend( hmmioIn, @ckIn, 0 ) <> 0 then
  begin
    Dispose( ppwfxInfo^ ); //?
    ppwfxInfo^ := nil;
    Exit;
  end;

  Result := S_OK;
end;
{$ENDIF}




{$IFNDEF START_EXTERNAL}
//------------------------------------------------------------------------------
// Имя:      WaveStartDataRead()
// Описание:
//------------------------------------------------------------------------------
function WaveStartDataRead( phmmioIn: PHMMIO; pckIn: PMMCKINFO;
                            pckInRIFF: PMMCKINFO ): HRESULT;
begin
  Result := E_FAIL;

  // Seek to the data
  if mmioSeek( phmmioIn^, pckInRIFF.dwDataOffset+SizeOf( FOURCC ),
               SEEK_SET ) = -1 then
     Exit;

  pckIn.ckid := mmioStringToFOURCC( 'd'+'a'+'t'+'a', 0 );
  if mmioDescend ( phmmioIn^, pckIn, pckInRIFF, MMIO_FINDCHUNK ) <> 0 then
     Exit;

  Result := S_OK;
end;
{$ENDIF}




{$IFNDEF WAVE_READ_EXTERNAL}
//------------------------------------------------------------------------------
// Имя:      WaveReadFile()
// Описание:
//------------------------------------------------------------------------------
function WaveReadFile( hmmioIn: HMMIO; cbRead: UINT; pbDest: PByte;
                       pckIn: PMMCKINFO; cbActualRead: PUINT ): HRESULT;
var
  mmioinfoIn: MMIOINFO;
  cbDataIn:   UINT;
  cT:         DWORD;
begin
  Result := E_FAIL;

  cbActualRead^ := 0;

  if mmioGetInfo( hmmioIn, @mmioinfoIn, 0 ) <> 0 then
     Exit;

  cbDataIn := cbRead;

  if cbDataIn > pckIn.cksize then
     cbDataIn := pckIn.cksize;

  pckIn.cksize := pckIn.cksize - cbDataIn; // SDK pckIn->ckSize -= cbDataIn

  cT := 0;

  while cT < cbDataIn do
  begin
    // Копируем байты из I/O в буффер
    if mmioinfoIn.pchNext = mmioinfoIn.pchEndRead then
    begin
      if mmioAdvance( hmmioIn, @mmioinfoIn, MMIO_READ ) <> 0 then
         Exit;

      if mmioinfoIn.pchNext = mmioinfoIn.pchEndRead then
         Exit;
    end;

    // Вот так в С++
    // *((BYTE*)pbDest+cT) = *((BYTE*)mmioinfoIn.pchNext);
    //    mmioinfoIn.pchNext++;

    // Может так:
    //( PByte( pbDest^ + cT ) )^ := ( PByte( mmioinfoIn.pchNext ) )^;

    Inc( mmioinfoIn.pchNext );
    Inc(cT);
  end;

  if mmioSetInfo( hmmioIn, @mmioinfoIn, 0 ) <> 0 then
  begin
    Result := E_FAIL; Exit;
  end;

  cbActualRead^ := cbDataIn;

  Result := S_OK;
end;
{$ENDIF}

end.





