通过lame_enc.dll实现将Wav转为mp3格式.wav要求是16bit

I spent some time to have Delphi interface correctly with the Lame-encoder-DLL, so I thought it a good idea to share the result, since I also could not find any good Delphi-code for this on the net.

The Lame-source comes with a rudimentary Delphi-header-file, but this has several issues, which I have tried to fix:

  • The file references unnecessary stuff, preventing compilation, easy to fix.
  • The encoding starts at the beginning of the wave-file, thereby encoding the header. This gives a noise at the beginning and can switch the stereo-channels. Fix: Offset the source into the data-section of the wave-file. Since this offset can vary, I have used the utility functions by Kambiz R. Khojasteh (http://www.delphiarea.com) to retrieve the necessary info using WinApi.MMSystem.
  • Lame suggests writing a VBR-Header to the file, even though it's CBR, I have changed the routine accordingly. This way devices can e.g. figure out the duration of the mp3-audio more easily.
  • Instead of file-handles I'm using TFileStream, that seems to speed up encoding considerably.

Usage:  EncodeWavToMP3(WaveFile, MP3File, Bitrate)

WaveFile needs to be 16-bit Stereo, but that could be adjusted.

Bitrate is a constant bitrate, for example 128. Support for VBR could be added.

If you use it and find something wrong, I'd like to know 🙂

总结一下,其实就是lame_enc.dall v3.99.3 for audacity专用版,直接下载地址为:Lame, lame_enc.dll and FFmpeg libraries for Audacity - Free and Safe downloads - LAME Websites eurorack blog and max4live blog 2023 - DO NOT CLICK GREEN DOWNLOAD BUTTONS

Here is the unit:

unit MP3ExportLame;

interface

Uses System.SysUtils, WinApi.Windows, System.Classes;

type
  // type definitions

  PHBE_STREAM = ^THBE_STREAM;
  THBE_STREAM = LongWord;
  BE_ERR = LongWord;

const
  // encoding formats

  BE_CONFIG_MP3 = 0;
  BE_CONFIG_LAME = 256;

  // error codes

  BE_ERR_SUCCESSFUL: LongWord = 0;
  BE_ERR_INVALID_FORMAT: LongWord = 1;
  BE_ERR_INVALID_FORMAT_PARAMETERS: LongWord = 2;
  BE_ERR_NO_MORE_HANDLES: LongWord = 3;
  BE_ERR_INVALID_HANDLE: LongWord = 4;

   // format specific variables

  BE_MP3_MODE_STEREO = 0;
  BE_MP3_MODE_DUALCHANNEL = 2;
  BE_MP3_MODE_MONO = 3;

  // other constants

  BE_MAX_HOMEPAGE = 256;

type

  TMP3 = packed record
    dwSampleRate: LongWord;
    byMode: Byte;
    wBitRate: Word;
    bPrivate: LongWord;
    bCRC: LongWord;
    bCopyright: LongWord;
    bOriginal: LongWord;
  end;

  TLHV1 = packed record
    // STRUCTURE INFORMATION
    dwStructVersion: DWORD;
    dwStructSize: DWORD;

    // BASIC ENCODER SETTINGS
    dwSampleRate: DWORD; // ALLOWED SAMPLERATE VALUES DEPENDS ON dwMPEGVersion
    dwReSampleRate: DWORD; // DOWNSAMPLERATE, 0=ENCODER DECIDES
    nMode: Integer;
    // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO
    dwBitrate: DWORD; // CBR bitrate, VBR min bitrate
    dwMaxBitrate: DWORD; // CBR ignored, VBR Max bitrate
    nQuality: Integer; // Quality setting (NORMAL,HIGH,LOW,VOICE)
    dwMpegVersion: DWORD; // MPEG-1 OR MPEG-2
    dwPsyModel: DWORD; // FUTURE USE, SET TO 0
    dwEmphasis: DWORD; // FUTURE USE, SET TO 0

    // BIT STREAM SETTINGS
    bPrivate: LONGBOOL; // Set Private Bit (TRUE/FALSE)
    bCRC: LONGBOOL; // Insert CRC (TRUE/FALSE)
    bCopyright: LONGBOOL; // Set Copyright Bit (TRUE/FALSE)
    bOriginal: LONGBOOL; // Set Original Bit (TRUE/FALSE_

    // VBR STUFF
    bWriteVBRHeader: LONGBOOL; // WRITE XING VBR HEADER (TRUE/FALSE)
    bEnableVBR: LONGBOOL; // USE VBR ENCODING (TRUE/FALSE)
    nVBRQuality: Integer; // VBR QUALITY 0..9

    btReserved: array [0 .. 255] of Byte; // FUTURE USE, SET TO 0
  end;

  TAAC = packed record
    dwSampleRate: LongWord;
    byMode: Byte;
    wBitRate: Word;
    byEncodingMethod: Byte;
  end;

  TFormat = packed record
    case Byte of
      1:
        (mp3: TMP3);
      2:
        (lhv1: TLHV1);
      3:
        (aac: TAAC);
  end;

  TBE_Config = packed record
    dwConfig: LongWord;
    format: TFormat;
  end;

  PBE_Config = ^TBE_Config;

  TBE_Version = record
    byDLLMajorVersion: Byte;
    byDLLMinorVersion: Byte;

    byMajorVersion: Byte;
    byMinorVersion: Byte;

    byDay: Byte;
    byMonth: Byte;
    wYear: Word;

    zHomePage: Array [0 .. BE_MAX_HOMEPAGE + 1] of Char;
  end;

  PBE_Version = ^TBE_Version;


//Headers for Lame_enc.dll (ver. 3.100)

Function beInitStream(var pbeConfig: TBE_Config; var dwSample: LongWord;
  var dwBufferSize: LongWord; var phbeStream: THBE_STREAM): BE_ERR; cdecl;
  external 'Lame_enc.dll';
Function beEncodeChunk(hbeStream: THBE_STREAM; nSamples: LongWord; var pSample;
  var pOutput; var pdwOutput: LongWord): BE_ERR; cdecl; external 'Lame_enc.dll';
Function beDeinitStream(hbeStream: THBE_STREAM; var pOutput;
  var pdwOutput: LongWord): BE_ERR; cdecl; external 'Lame_enc.dll';
Function beCloseStream(hbeStream: THBE_STREAM): BE_ERR; cdecl;
  external 'Lame_enc.dll';
Procedure beVersion(var pbeVersion: TBE_Version); cdecl;
  external 'Lame_enc.dll';
// Added header for beWriteVBRHeader
Procedure beWriteVBRHeader(MP3FileName: pAnsiChar); cdecl;
  external 'Lame_enc.dll';

Procedure EncodeWavToMP3(WaveFile, MP3File: string; BitRate: Integer);
// BitRate 128 192 256 etc.

implementation

uses WinApi.MMSystem;

{ ---------------------------------------- }

{ The following functions retrieve the necessary info from the input-wave-file. }
{ Source: }
{ WaveUtils - Utility functions and data types }
{ by Kambiz R. Khojasteh }
{ }
{ kambiz@delphiarea.com }
{ http://www.delphiarea.com }

function mmioStreamProc(lpmmIOInfo: PMMIOInfo; uMsg, lParam1, lParam2: DWORD)
  : LRESULT; stdcall;
var
  Stream: TStream;
begin
  if Assigned(lpmmIOInfo) and (lpmmIOInfo^.adwInfo[0] <> 0) then
  begin
    Stream := TStream(lpmmIOInfo^.adwInfo[0]);
    case uMsg of
      MMIOM_OPEN:
        begin
          if TObject(lpmmIOInfo^.adwInfo[0]) is TStream then
          begin
            Stream.Seek(0, SEEK_SET);
            lpmmIOInfo^.lDiskOffset := 0;
            Result := MMSYSERR_NOERROR;
          end
          else
            Result := -1;
        end;
      MMIOM_CLOSE:
        Result := MMSYSERR_NOERROR;
      MMIOM_SEEK:
        try
          if lParam2 = SEEK_CUR then
            Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
          Result := Stream.Seek(lParam1, lParam2);
          lpmmIOInfo^.lDiskOffset := Result;
        except
          Result := -1;
        end;
      MMIOM_READ:
        try
          Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
          Result := Stream.Read(Pointer(lParam1)^, lParam2);
          lpmmIOInfo^.lDiskOffset := Stream.Seek(0, SEEK_CUR);
        except
          Result := -1;
        end;
      MMIOM_WRITE, MMIOM_WRITEFLUSH:
        try
          Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
          Result := Stream.Write(Pointer(lParam1)^, lParam2);
          lpmmIOInfo^.lDiskOffset := Stream.Seek(0, SEEK_CUR);
        except
          Result := -1;
        end
    else
      Result := MMSYSERR_NOERROR;
    end;
  end
  else
    Result := -1;
end;

function OpenStreamWaveAudio(Stream: TStream): HMMIO;
var
  mmIOInfo: TMMIOINFO;
begin
  FillChar(mmIOInfo, SizeOf(mmIOInfo), 0);
  mmIOInfo.pIOProc := @mmioStreamProc;
  mmIOInfo.adwInfo[0] := DWORD(Stream);
  Result := mmioOpen(nil, @mmIOInfo, MMIO_READWRITE);
end;

function GetWaveAudioInfo(mmIO: HMMIO; var pWaveFormat: PWaveFormatEx;
  var DataSize, DataOffset: DWORD): Boolean;

  function GetWaveFormat(const ckRIFF: TMMCKInfo): Boolean;
  var
    ckFormat: TMMCKInfo;
  begin
    Result := False;
    ckFormat.ckid := mmioStringToFOURCC('fmt', 0);
    if (mmioDescend(mmIO, @ckFormat, @ckRIFF, MMIO_FINDCHUNK)
      = MMSYSERR_NOERROR) and (ckFormat.cksize >= SizeOf(TWaveFormat)) then
    begin
      if ckFormat.cksize < SizeOf(TWaveFormatEx) then
      begin
        GetMem(pWaveFormat, SizeOf(TWaveFormatEx));
        FillChar(pWaveFormat^, SizeOf(TWaveFormatEx), 0);
      end
      else
        GetMem(pWaveFormat, ckFormat.cksize);
      Result := (mmioRead(mmIO, pAnsiChar(pWaveFormat), ckFormat.cksize)
        = Integer(ckFormat.cksize));
    end;
  end;

  function GetWaveData(const ckRIFF: TMMCKInfo): Boolean;
  var
    ckData: TMMCKInfo;
  begin
    Result := False;
    ckData.ckid := mmioStringToFOURCC('data', 0);
    if (mmioDescend(mmIO, @ckData, @ckRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR)
    then
    begin
      DataSize := ckData.cksize;
      DataOffset := ckData.dwDataOffset;
      Result := True;
    end;
  end;

var
  ckRIFF: TMMCKInfo;
  OrgPos: Integer;
begin
  Result := False;
  OrgPos := mmioSeek(mmIO, 0, SEEK_CUR);
  try
    mmioSeek(mmIO, 0, SEEK_SET);
    ckRIFF.fccType := mmioStringToFOURCC('WAVE', 0);
    if (mmioDescend(mmIO, @ckRIFF, nil, MMIO_FINDRIFF) = MMSYSERR_NOERROR) then
    begin
      pWaveFormat := nil;
      if GetWaveFormat(ckRIFF) and GetWaveData(ckRIFF) then
        Result := True
      else if Assigned(pWaveFormat) then
        ReallocMem(pWaveFormat, 0);
    end
  finally
    mmioSeek(mmIO, OrgPos, SEEK_SET);
  end;
end;

function GetStreamWaveAudioInfo(Stream: TStream; var pWaveFormat: PWaveFormatEx;
  var DataSize, DataOffset: DWORD): Boolean;
var
  mmIO: HMMIO;
begin
  Result := False;
  if Stream.Size <> 0 then
  begin
    mmIO := OpenStreamWaveAudio(Stream);
    if mmIO <> 0 then
      try
        Result := GetWaveAudioInfo(mmIO, pWaveFormat, DataSize, DataOffset);
      finally
        mmioClose(mmIO, MMIO_FHOPEN);
      end;
  end;
end;

Procedure EncodeWavToMP3(WaveFile, MP3File: string; BitRate: Integer);
var
  beConfig: TBE_Config;
  dwSamples, dwSamplesMP3: LongWord;
  hbeStream: THBE_STREAM;
  error: BE_ERR;
  pBuffer: PSmallInt;
  pMP3Buffer: PByte;

  done: LongWord;
  dwWrite: LongWord;
  ToRead: LongWord;
  ToWrite: LongWord;

  // changed from THandle to TFileStream
  fs, ft: TFileStream;
  TotalSize: DWORD;

  // variables to hold the wave info necessary for encoding
  pWaveFormat: PWaveFormatEx;
  DataOffset, DataSize, InputSampleRate: DWORD;

begin
  beConfig.dwConfig := BE_CONFIG_LAME;
  fs := TFileStream.Create(WaveFile, fmOpenRead or fmShareDenyWrite);
  ft := TFileStream.Create(MP3File, fmCreate or fmShareDenyWrite);
  try
    TotalSize := fs.Size;

    // obtain info from source wave file
    try
      if not GetStreamWaveAudioInfo(fs, pWaveFormat, DataSize, DataOffset) then
        raise Exception.Create
          ('Unable to obtain necessary info from wave file.');
      if (pWaveFormat.nChannels <> 2) or (pWaveFormat.wBitsPerSample <> 16) then
        raise Exception.Create('Wave format must be 16bit Stereo.');
      InputSampleRate := pWaveFormat.nSamplesPerSec;
    finally
      FreeMem(pWaveFormat);
    end;

    // Structure information
    beConfig.format.lhv1.dwStructVersion := 1;
    beConfig.format.lhv1.dwStructSize := SizeOf(beConfig);
    // Basic encoder setting
    beConfig.format.lhv1.dwSampleRate := InputSampleRate;
    beConfig.format.lhv1.dwReSampleRate := InputSampleRate;
    beConfig.format.lhv1.nMode := BE_MP3_MODE_STEREO;
    beConfig.format.lhv1.dwBitrate := BitRate;
    beConfig.format.lhv1.dwMaxBitrate := BitRate;
    beConfig.format.lhv1.nQuality := 4;
    beConfig.format.lhv1.dwMpegVersion := 1;
    // MPEG1
    beConfig.format.lhv1.dwPsyModel := 0;
    beConfig.format.lhv1.dwEmphasis := 0;
    // Bit Stream Settings
    beConfig.format.lhv1.bPrivate := False;
    beConfig.format.lhv1.bCRC := True;
    beConfig.format.lhv1.bCopyright := True;
    beConfig.format.lhv1.bOriginal := True;
    // VBR Stuff
    // Have it write a VBRHeader, as recommended by Lame, even though it's CBR
    beConfig.format.lhv1.bWriteVBRHeader := True;
    beConfig.format.lhv1.bEnableVBR := False;
    beConfig.format.lhv1.nVBRQuality := 0;

    error := beInitStream(beConfig, dwSamples, dwSamplesMP3, hbeStream);
    if error = BE_ERR_SUCCESSFUL then
    begin
      pBuffer := AllocMem(dwSamples * 2);
      pMP3Buffer := AllocMem(dwSamplesMP3);
      try
        // Position the source file stream at the beginning of the PCM-data:
        done := DataOffset;
        fs.Seek(DataOffset, soFromBeginning);
        While (done < TotalSize) do
        begin
          if (done + dwSamples * 2 < TotalSize) then
            ToRead := dwSamples * 2
          else
          begin
            ToRead := TotalSize - done;
            FillChar(pBuffer^, dwSamples * 2, 0);
          end;

          fs.Read(pBuffer^, ToRead);

          error := beEncodeChunk(hbeStream, ToRead div 2, pBuffer^,
            pMP3Buffer^, ToWrite);

          if error <> BE_ERR_SUCCESSFUL then
          begin
            beCloseStream(hbeStream);
            raise Exception.Create('Encoding Error');
          end;

          ft.Write(pMP3Buffer^, ToWrite);

          done := done + ToRead;

        end;

        error := beDeinitStream(hbeStream, pMP3Buffer^, dwWrite);

        if error <> BE_ERR_SUCCESSFUL then
        begin
          beCloseStream(hbeStream);
          raise Exception.Create('Close Error');
        end;

        if dwWrite <> 0 then
        begin
          ft.Write(pMP3Buffer^, dwWrite);
        end;

        error := beCloseStream(hbeStream);
        if error <> BE_ERR_SUCCESSFUL then
        begin
          raise Exception.Create('Close Error');
        end;
      finally
        FreeMem(pBuffer);
        FreeMem(pMP3Buffer);
      end;
    end
    else
    begin
      Raise Exception.Create('InitStream failure');
    end;
  finally
    fs.free;
    ft.free;
  end;
  beWriteVBRHeader(pAnsiChar(AnsiString(MP3File)));
end;

end.

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
lame_enc.dllLAME MP3 编码器的动态链接库,它包含了编码 MP3 格式所需的函数和数据。你可以在你的 C++ 代码中调用这些函数来完成 MP3 编码的任务。 以下是使用 lame_enc.dll 编码 MP3 文件的简单步骤: 1. 下载并安装 LAME MP3 编码器,它会包含 lame_enc.dll 文件。 2. 将 lame_enc.dll 文件复制到你的 C++ 项目的 Debug 或 Release 目录中。 3. 在你的 C++ 代码中引用 lame.h 头文件,并调用 lame_init() 函数来初始化编码器。 4. 使用 lame_set_in_samplerate()、lame_set_num_channels() 等函数设置编码器的参数。 5. 使用 lame_encode_buffer_interleaved() 或 lame_encode_buffer() 函数将 PCM 音频数据编码为 MP3 格式。 6. 使用 lame_close() 函数关闭编码器并释放资源。 以下是一个简单的示例代码,用于将 PCM 音频数据编码为 MP3 格式: ```c++ #include "lame.h" #include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]) { // 初始化编码器 lame_global_flags* gfp = lame_init(); if (gfp == NULL) { printf("Failed to initialize LAME encoder.\n"); exit(-1); } // 设置编码器参数 lame_set_in_samplerate(gfp, 44100); lame_set_num_channels(gfp, 2); lame_set_brate(gfp, 128); // 打开输出文件 FILE* outfile = fopen("output.mp3", "wb"); if (outfile == NULL) { printf("Failed to open output file.\n"); exit(-1); } // 编码 PCM 数据 short pcm_buffer[8192]; unsigned char mp3_buffer[8192]; int pcm_samples = 0; int mp3_bytes = 0; while (pcm_samples < total_samples) { // 从输入文件读取 PCM 数据 fread(pcm_buffer, sizeof(short), 8192, infile); pcm_samples += 8192; // 编码 PCM 数据为 MP3 mp3_bytes = lame_encode_buffer_interleaved(gfp, pcm_buffer, 8192, mp3_buffer, 8192); // 将编码后的 MP3 数据写入输出文件 fwrite(mp3_buffer, 1, mp3_bytes, outfile); } // 关闭输出文件 fclose(outfile); // 关闭编码器 lame_close(gfp); return 0; } ``` 在上面的代码中,我们使用 lame_init() 函数初始化编码器,并使用 lame_set_in_samplerate() 和 lame_set_num_channels() 函数设置编码器的参数。然后我们使用 lame_encode_buffer_interleaved() 函数将 PCM 数据编码为 MP3 格式,并将编码后的 MP3 数据写入输出文件。最后我们使用 lame_close() 函数关闭编码器并释放资源。 请注意,上面的代码仅供参考,实际使用时需要根据你的需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蝈蝈(GuoGuo)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值