• 通过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:

    1. unit MP3ExportLame;
    2. interface
    3. Uses System.SysUtils, WinApi.Windows, System.Classes;
    4. type
    5. // type definitions
    6. PHBE_STREAM = ^THBE_STREAM;
    7. THBE_STREAM = LongWord;
    8. BE_ERR = LongWord;
    9. const
    10. // encoding formats
    11. BE_CONFIG_MP3 = 0;
    12. BE_CONFIG_LAME = 256;
    13. // error codes
    14. BE_ERR_SUCCESSFUL: LongWord = 0;
    15. BE_ERR_INVALID_FORMAT: LongWord = 1;
    16. BE_ERR_INVALID_FORMAT_PARAMETERS: LongWord = 2;
    17. BE_ERR_NO_MORE_HANDLES: LongWord = 3;
    18. BE_ERR_INVALID_HANDLE: LongWord = 4;
    19. // format specific variables
    20. BE_MP3_MODE_STEREO = 0;
    21. BE_MP3_MODE_DUALCHANNEL = 2;
    22. BE_MP3_MODE_MONO = 3;
    23. // other constants
    24. BE_MAX_HOMEPAGE = 256;
    25. type
    26. TMP3 = packed record
    27. dwSampleRate: LongWord;
    28. byMode: Byte;
    29. wBitRate: Word;
    30. bPrivate: LongWord;
    31. bCRC: LongWord;
    32. bCopyright: LongWord;
    33. bOriginal: LongWord;
    34. end;
    35. TLHV1 = packed record
    36. // STRUCTURE INFORMATION
    37. dwStructVersion: DWORD;
    38. dwStructSize: DWORD;
    39. // BASIC ENCODER SETTINGS
    40. dwSampleRate: DWORD; // ALLOWED SAMPLERATE VALUES DEPENDS ON dwMPEGVersion
    41. dwReSampleRate: DWORD; // DOWNSAMPLERATE, 0=ENCODER DECIDES
    42. nMode: Integer;
    43. // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO
    44. dwBitrate: DWORD; // CBR bitrate, VBR min bitrate
    45. dwMaxBitrate: DWORD; // CBR ignored, VBR Max bitrate
    46. nQuality: Integer; // Quality setting (NORMAL,HIGH,LOW,VOICE)
    47. dwMpegVersion: DWORD; // MPEG-1 OR MPEG-2
    48. dwPsyModel: DWORD; // FUTURE USE, SET TO 0
    49. dwEmphasis: DWORD; // FUTURE USE, SET TO 0
    50. // BIT STREAM SETTINGS
    51. bPrivate: LONGBOOL; // Set Private Bit (TRUE/FALSE)
    52. bCRC: LONGBOOL; // Insert CRC (TRUE/FALSE)
    53. bCopyright: LONGBOOL; // Set Copyright Bit (TRUE/FALSE)
    54. bOriginal: LONGBOOL; // Set Original Bit (TRUE/FALSE_
    55. // VBR STUFF
    56. bWriteVBRHeader: LONGBOOL; // WRITE XING VBR HEADER (TRUE/FALSE)
    57. bEnableVBR: LONGBOOL; // USE VBR ENCODING (TRUE/FALSE)
    58. nVBRQuality: Integer; // VBR QUALITY 0..9
    59. btReserved: array [0 .. 255] of Byte; // FUTURE USE, SET TO 0
    60. end;
    61. TAAC = packed record
    62. dwSampleRate: LongWord;
    63. byMode: Byte;
    64. wBitRate: Word;
    65. byEncodingMethod: Byte;
    66. end;
    67. TFormat = packed record
    68. case Byte of
    69. 1:
    70. (mp3: TMP3);
    71. 2:
    72. (lhv1: TLHV1);
    73. 3:
    74. (aac: TAAC);
    75. end;
    76. TBE_Config = packed record
    77. dwConfig: LongWord;
    78. format: TFormat;
    79. end;
    80. PBE_Config = ^TBE_Config;
    81. TBE_Version = record
    82. byDLLMajorVersion: Byte;
    83. byDLLMinorVersion: Byte;
    84. byMajorVersion: Byte;
    85. byMinorVersion: Byte;
    86. byDay: Byte;
    87. byMonth: Byte;
    88. wYear: Word;
    89. zHomePage: Array [0 .. BE_MAX_HOMEPAGE + 1] of Char;
    90. end;
    91. PBE_Version = ^TBE_Version;
    92. //Headers for Lame_enc.dll (ver. 3.100)
    93. Function beInitStream(var pbeConfig: TBE_Config; var dwSample: LongWord;
    94. var dwBufferSize: LongWord; var phbeStream: THBE_STREAM): BE_ERR; cdecl;
    95. external 'Lame_enc.dll';
    96. Function beEncodeChunk(hbeStream: THBE_STREAM; nSamples: LongWord; var pSample;
    97. var pOutput; var pdwOutput: LongWord): BE_ERR; cdecl; external 'Lame_enc.dll';
    98. Function beDeinitStream(hbeStream: THBE_STREAM; var pOutput;
    99. var pdwOutput: LongWord): BE_ERR; cdecl; external 'Lame_enc.dll';
    100. Function beCloseStream(hbeStream: THBE_STREAM): BE_ERR; cdecl;
    101. external 'Lame_enc.dll';
    102. Procedure beVersion(var pbeVersion: TBE_Version); cdecl;
    103. external 'Lame_enc.dll';
    104. // Added header for beWriteVBRHeader
    105. Procedure beWriteVBRHeader(MP3FileName: pAnsiChar); cdecl;
    106. external 'Lame_enc.dll';
    107. Procedure EncodeWavToMP3(WaveFile, MP3File: string; BitRate: Integer);
    108. // BitRate 128 192 256 etc.
    109. implementation
    110. uses WinApi.MMSystem;
    111. { ---------------------------------------- }
    112. { The following functions retrieve the necessary info from the input-wave-file. }
    113. { Source: }
    114. { WaveUtils - Utility functions and data types }
    115. { by Kambiz R. Khojasteh }
    116. { }
    117. { kambiz@delphiarea.com }
    118. { http://www.delphiarea.com }
    119. function mmioStreamProc(lpmmIOInfo: PMMIOInfo; uMsg, lParam1, lParam2: DWORD)
    120. : LRESULT; stdcall;
    121. var
    122. Stream: TStream;
    123. begin
    124. if Assigned(lpmmIOInfo) and (lpmmIOInfo^.adwInfo[0] <> 0) then
    125. begin
    126. Stream := TStream(lpmmIOInfo^.adwInfo[0]);
    127. case uMsg of
    128. MMIOM_OPEN:
    129. begin
    130. if TObject(lpmmIOInfo^.adwInfo[0]) is TStream then
    131. begin
    132. Stream.Seek(0, SEEK_SET);
    133. lpmmIOInfo^.lDiskOffset := 0;
    134. Result := MMSYSERR_NOERROR;
    135. end
    136. else
    137. Result := -1;
    138. end;
    139. MMIOM_CLOSE:
    140. Result := MMSYSERR_NOERROR;
    141. MMIOM_SEEK:
    142. try
    143. if lParam2 = SEEK_CUR then
    144. Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
    145. Result := Stream.Seek(lParam1, lParam2);
    146. lpmmIOInfo^.lDiskOffset := Result;
    147. except
    148. Result := -1;
    149. end;
    150. MMIOM_READ:
    151. try
    152. Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
    153. Result := Stream.Read(Pointer(lParam1)^, lParam2);
    154. lpmmIOInfo^.lDiskOffset := Stream.Seek(0, SEEK_CUR);
    155. except
    156. Result := -1;
    157. end;
    158. MMIOM_WRITE, MMIOM_WRITEFLUSH:
    159. try
    160. Stream.Seek(lpmmIOInfo^.lDiskOffset, SEEK_SET);
    161. Result := Stream.Write(Pointer(lParam1)^, lParam2);
    162. lpmmIOInfo^.lDiskOffset := Stream.Seek(0, SEEK_CUR);
    163. except
    164. Result := -1;
    165. end
    166. else
    167. Result := MMSYSERR_NOERROR;
    168. end;
    169. end
    170. else
    171. Result := -1;
    172. end;
    173. function OpenStreamWaveAudio(Stream: TStream): HMMIO;
    174. var
    175. mmIOInfo: TMMIOINFO;
    176. begin
    177. FillChar(mmIOInfo, SizeOf(mmIOInfo), 0);
    178. mmIOInfo.pIOProc := @mmioStreamProc;
    179. mmIOInfo.adwInfo[0] := DWORD(Stream);
    180. Result := mmioOpen(nil, @mmIOInfo, MMIO_READWRITE);
    181. end;
    182. function GetWaveAudioInfo(mmIO: HMMIO; var pWaveFormat: PWaveFormatEx;
    183. var DataSize, DataOffset: DWORD): Boolean;
    184. function GetWaveFormat(const ckRIFF: TMMCKInfo): Boolean;
    185. var
    186. ckFormat: TMMCKInfo;
    187. begin
    188. Result := False;
    189. ckFormat.ckid := mmioStringToFOURCC('fmt', 0);
    190. if (mmioDescend(mmIO, @ckFormat, @ckRIFF, MMIO_FINDCHUNK)
    191. = MMSYSERR_NOERROR) and (ckFormat.cksize >= SizeOf(TWaveFormat)) then
    192. begin
    193. if ckFormat.cksize < SizeOf(TWaveFormatEx) then
    194. begin
    195. GetMem(pWaveFormat, SizeOf(TWaveFormatEx));
    196. FillChar(pWaveFormat^, SizeOf(TWaveFormatEx), 0);
    197. end
    198. else
    199. GetMem(pWaveFormat, ckFormat.cksize);
    200. Result := (mmioRead(mmIO, pAnsiChar(pWaveFormat), ckFormat.cksize)
    201. = Integer(ckFormat.cksize));
    202. end;
    203. end;
    204. function GetWaveData(const ckRIFF: TMMCKInfo): Boolean;
    205. var
    206. ckData: TMMCKInfo;
    207. begin
    208. Result := False;
    209. ckData.ckid := mmioStringToFOURCC('data', 0);
    210. if (mmioDescend(mmIO, @ckData, @ckRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR)
    211. then
    212. begin
    213. DataSize := ckData.cksize;
    214. DataOffset := ckData.dwDataOffset;
    215. Result := True;
    216. end;
    217. end;
    218. var
    219. ckRIFF: TMMCKInfo;
    220. OrgPos: Integer;
    221. begin
    222. Result := False;
    223. OrgPos := mmioSeek(mmIO, 0, SEEK_CUR);
    224. try
    225. mmioSeek(mmIO, 0, SEEK_SET);
    226. ckRIFF.fccType := mmioStringToFOURCC('WAVE', 0);
    227. if (mmioDescend(mmIO, @ckRIFF, nil, MMIO_FINDRIFF) = MMSYSERR_NOERROR) then
    228. begin
    229. pWaveFormat := nil;
    230. if GetWaveFormat(ckRIFF) and GetWaveData(ckRIFF) then
    231. Result := True
    232. else if Assigned(pWaveFormat) then
    233. ReallocMem(pWaveFormat, 0);
    234. end
    235. finally
    236. mmioSeek(mmIO, OrgPos, SEEK_SET);
    237. end;
    238. end;
    239. function GetStreamWaveAudioInfo(Stream: TStream; var pWaveFormat: PWaveFormatEx;
    240. var DataSize, DataOffset: DWORD): Boolean;
    241. var
    242. mmIO: HMMIO;
    243. begin
    244. Result := False;
    245. if Stream.Size <> 0 then
    246. begin
    247. mmIO := OpenStreamWaveAudio(Stream);
    248. if mmIO <> 0 then
    249. try
    250. Result := GetWaveAudioInfo(mmIO, pWaveFormat, DataSize, DataOffset);
    251. finally
    252. mmioClose(mmIO, MMIO_FHOPEN);
    253. end;
    254. end;
    255. end;
    256. Procedure EncodeWavToMP3(WaveFile, MP3File: string; BitRate: Integer);
    257. var
    258. beConfig: TBE_Config;
    259. dwSamples, dwSamplesMP3: LongWord;
    260. hbeStream: THBE_STREAM;
    261. error: BE_ERR;
    262. pBuffer: PSmallInt;
    263. pMP3Buffer: PByte;
    264. done: LongWord;
    265. dwWrite: LongWord;
    266. ToRead: LongWord;
    267. ToWrite: LongWord;
    268. // changed from THandle to TFileStream
    269. fs, ft: TFileStream;
    270. TotalSize: DWORD;
    271. // variables to hold the wave info necessary for encoding
    272. pWaveFormat: PWaveFormatEx;
    273. DataOffset, DataSize, InputSampleRate: DWORD;
    274. begin
    275. beConfig.dwConfig := BE_CONFIG_LAME;
    276. fs := TFileStream.Create(WaveFile, fmOpenRead or fmShareDenyWrite);
    277. ft := TFileStream.Create(MP3File, fmCreate or fmShareDenyWrite);
    278. try
    279. TotalSize := fs.Size;
    280. // obtain info from source wave file
    281. try
    282. if not GetStreamWaveAudioInfo(fs, pWaveFormat, DataSize, DataOffset) then
    283. raise Exception.Create
    284. ('Unable to obtain necessary info from wave file.');
    285. if (pWaveFormat.nChannels <> 2) or (pWaveFormat.wBitsPerSample <> 16) then
    286. raise Exception.Create('Wave format must be 16bit Stereo.');
    287. InputSampleRate := pWaveFormat.nSamplesPerSec;
    288. finally
    289. FreeMem(pWaveFormat);
    290. end;
    291. // Structure information
    292. beConfig.format.lhv1.dwStructVersion := 1;
    293. beConfig.format.lhv1.dwStructSize := SizeOf(beConfig);
    294. // Basic encoder setting
    295. beConfig.format.lhv1.dwSampleRate := InputSampleRate;
    296. beConfig.format.lhv1.dwReSampleRate := InputSampleRate;
    297. beConfig.format.lhv1.nMode := BE_MP3_MODE_STEREO;
    298. beConfig.format.lhv1.dwBitrate := BitRate;
    299. beConfig.format.lhv1.dwMaxBitrate := BitRate;
    300. beConfig.format.lhv1.nQuality := 4;
    301. beConfig.format.lhv1.dwMpegVersion := 1;
    302. // MPEG1
    303. beConfig.format.lhv1.dwPsyModel := 0;
    304. beConfig.format.lhv1.dwEmphasis := 0;
    305. // Bit Stream Settings
    306. beConfig.format.lhv1.bPrivate := False;
    307. beConfig.format.lhv1.bCRC := True;
    308. beConfig.format.lhv1.bCopyright := True;
    309. beConfig.format.lhv1.bOriginal := True;
    310. // VBR Stuff
    311. // Have it write a VBRHeader, as recommended by Lame, even though it's CBR
    312. beConfig.format.lhv1.bWriteVBRHeader := True;
    313. beConfig.format.lhv1.bEnableVBR := False;
    314. beConfig.format.lhv1.nVBRQuality := 0;
    315. error := beInitStream(beConfig, dwSamples, dwSamplesMP3, hbeStream);
    316. if error = BE_ERR_SUCCESSFUL then
    317. begin
    318. pBuffer := AllocMem(dwSamples * 2);
    319. pMP3Buffer := AllocMem(dwSamplesMP3);
    320. try
    321. // Position the source file stream at the beginning of the PCM-data:
    322. done := DataOffset;
    323. fs.Seek(DataOffset, soFromBeginning);
    324. While (done < TotalSize) do
    325. begin
    326. if (done + dwSamples * 2 < TotalSize) then
    327. ToRead := dwSamples * 2
    328. else
    329. begin
    330. ToRead := TotalSize - done;
    331. FillChar(pBuffer^, dwSamples * 2, 0);
    332. end;
    333. fs.Read(pBuffer^, ToRead);
    334. error := beEncodeChunk(hbeStream, ToRead div 2, pBuffer^,
    335. pMP3Buffer^, ToWrite);
    336. if error <> BE_ERR_SUCCESSFUL then
    337. begin
    338. beCloseStream(hbeStream);
    339. raise Exception.Create('Encoding Error');
    340. end;
    341. ft.Write(pMP3Buffer^, ToWrite);
    342. done := done + ToRead;
    343. end;
    344. error := beDeinitStream(hbeStream, pMP3Buffer^, dwWrite);
    345. if error <> BE_ERR_SUCCESSFUL then
    346. begin
    347. beCloseStream(hbeStream);
    348. raise Exception.Create('Close Error');
    349. end;
    350. if dwWrite <> 0 then
    351. begin
    352. ft.Write(pMP3Buffer^, dwWrite);
    353. end;
    354. error := beCloseStream(hbeStream);
    355. if error <> BE_ERR_SUCCESSFUL then
    356. begin
    357. raise Exception.Create('Close Error');
    358. end;
    359. finally
    360. FreeMem(pBuffer);
    361. FreeMem(pMP3Buffer);
    362. end;
    363. end
    364. else
    365. begin
    366. Raise Exception.Create('InitStream failure');
    367. end;
    368. finally
    369. fs.free;
    370. ft.free;
    371. end;
    372. beWriteVBRHeader(pAnsiChar(AnsiString(MP3File)));
    373. end;
    374. end.

  • 相关阅读:
    微服务架构最佳实践:消息队列
    工程师职称评审有关安排
    斯坦福大学吴恩达教授最新来信:AI, GPU和芯片的未来
    Android入门第9天-Android读本地JSON文件并显示
    Linux 之 arm linux 与 windows 使用 tftp 进行文件传输的简单整理
    Discuz论坛帖子标题随机高亮颜色,拒绝千篇一律!
    计算机毕业设计之java+ssm基于android的家庭理财系统app
    Web开发:Web开发中的域概念整理与解读
    MySQL 字段的基本操作:添加、修改和删除字段(详解)
    Log4j 2.17.0 再曝漏洞,但不要惊慌
  • 原文地址:https://blog.csdn.net/delphigbg/article/details/139455746