📄 tplaywaveunit.~pas
字号:
WaveFmt.nSamplesPerSec := 22050; //采样频率为 22050 KHZ
//8000;
WaveFmt.nAvgBytesPerSec := 44100; //平均数据传输率
//00;
WaveFmt.wBitsPerSample := 16; //采样大小
WaveFmt.nBlockAlign := 2; //块对齐方式
if not((Self.pFormat^.nChannels = WaveFmt.nChannels)
and (Self.pFormat^.nSamplesPerSec = WaveFmt.nSamplesPerSec)
and (Self.pFormat^.wBitsPerSample = WaveFmt.wBitsPerSample)) then
begin
//进行数据转换
Wave_Save_ConvertFile(Self.BackFileName, Self.DataLength, Wavefmt,
Self.pFormat^, Self.hio, Self.ShowPoistrator);
Self.CloseWaveFile; //关闭打开的音频文件
end //文件复制
else CopyFile(PChar(Value), PChar(Self.BackFileName), True);
if Self.DataLength <= 0 then
begin
Self.FFileName := PChar('');
Self.FNowPlay := CPNONE; //没有任何状态
Exit;
end;
end
else begin
Self.FFileName := PChar('');
Self.FNowPlay := CPNONE; //没有任何状态
Exit;
end;
if Self.OpenFileName(PChar(Self.BackFileName)) then
begin
Self.FFileName := PChar(Value);
Self.FNowPlay := CPSTOPING; //当前为停止状态
end
else begin
Self.FFileName := PChar('');
Self.FNowPlay := CPNONE;
end;
//进度显示
if Self.ShowMessageParBar <> NIL then
begin
Self.ShowMessageParBar.Position := 100;
end;
if Assigned(Self.WaveLoopEvent) then
Self.WaveLoopEvent(WA_EN_OPENFILE, 100, 0, 100);
end;
//***********************************************************************//
// //
// 控制画波形的时间周期 //
// 参数: 无 //
// 返回值: 无 //
// //
//***********************************************************************//
procedure TCMPlayWave.SDTime(Sender: TObject);
begin
TTimer(Sender).Enabled := False;
TTimer(Sender).Tag := 1; //可以画波形了
end;
//***********************************************************************//
// //
// 音频播放子过程 //
// 参数: 无 //
// 返回值: 无 //
// //
//***********************************************************************//
procedure TCMPlayWave.PlayWave;
var
i: Integer;
lpWaveHdr: PWaveHdr; //指向WAVEHDR结构的指针
tm: TMMTime;
SetDrawTime: TTimer;
TempAv: LongInt;
begin
SetDrawTime := TTimer.Create(Application); //用于控制画波形的时间周期
SetDrawTime.Enabled := False;
SetDrawTime.Interval := 75;
SetDrawTime.Tag := 1;
SetDrawTime.OnTimer := Self.SDTime;
Self.SetMemTime.Tag := 1;
Self.PlayLoop := Self.PlayDataLength DIV LoadMemSize;
Self.MODInt := Self.PlayDataLength MOD LoadMemSize;
if Self.MODInt <> 0 then
Self.PlayLoop := Self.PlayLoop + 1;
Self.FNowPlay := CPPLAYING; //正处于播放状态
//发送开始播放的消息
SendMessage(Application.MainForm.Handle, CM_PLAYBEGIN, 0, 0);
// Self.FWavePlayKM := 40;
TempAv := Self.pFormat.nSamplesPerSec;
Try
// if (Self.FWavePlayKM MOD 2) = 1 then
// Self.FWavePlayKM := Self.FWavePlayKM + 1;
if Self.FWavePlayKM <> 0 then
begin
// Self.pFormat.nAvgBytesPerSec := Self.pFormat.nAvgBytesPerSec * Self.FWavePlayKM DIV 100;
Self.pFormat.nSamplesPerSec := Self.pFormat.nSamplesPerSec * Self.FWavePlayKM DIV 100;
// Self.pFormat.nBlockAlign := Self.pFormat.nSamplesPerSec * Self.pFormat.nChannels DIV 8;
end;
// SetDrawTime.Interval := SetDrawTime.Interval * ABS(Self.WavePlayKM - 100);
//打开音频设备
if WaveOutOpen(@Self.hwo, WAVE_MAPPER, Self.pFormat, 0, 0, 0) <> 0 then
raise EMMIO.Create('打开音频设备错!');
//开辟内存
lpWaveHdr := PWaveHdr(GlobalAllocPtr(GMEM_MOVEABLE OR GMEM_SHARE, DWORD(Sizeof(WAVEHDR))));
if lpWaveHdr = NIL then
raise EMMIO.Create('不能分配内存!');
MMioSeek(Self.hio, Self.DataLength - Self.GotoPosition, SEEK_END); //把文件指针移到音频数据的指定开始处
Self.SetPlayMem(Self.SetMemTime); //读取音频数据
lpWaveHdr.lpData := Self.FirstData; //音频数据
lpWaveHdr.dwBufferLength := DWORD(Self.PlaySize); //音频数据长度
for i := 1 to Self.PlayLoop do //双缓存交换播放
begin
if i > 1 then
begin
lpWaveHdr.lpData := NIL;
if Self.SetMemTime.Tag MOD 2 = 0 then
lpWaveHdr.lpData := Self.FirstData
else
lpWaveHdr.lpData := Self.NextData;
lpWaveHdr.dwBufferLength := DWORD(Self.PlaySize); //音频数据长度
end;
lpWaveHdr.dwFlags := 0;
lpWaveHdr.dwLoops := 0;
//生成音频信息头
if WaveOutPrepareHeader(Self.hwo, lpWaveHdr, SizeOf(WAVEHDR)) <> 0 then
raise EMMIO.Create('不能准备数据!');
if Self.PlaySize > 1 then
Self.SetMemTime.Enabled := True; //读取音频数据
//向音频设备写音频数据
if WaveOutWrite(Self.hwo, lpWaveHdr, SizeOf(WAVEHDR)) <> 0 then
raise EMMIO.Create('写数据错!');
SetDrawTime.Tag := 1;
//数据是否已播放完
while Boolean(NOT lpWaveHdr.dwFlags AND WHDR_DONE) do
begin
Application.ProcessMessages; //处理 Windows 消息
if (Self.FNowPlay = CPSTOPING) OR (Self.FNowPlay = CPNONE) then
break;
if Self.FNowPlay = CPPLAYING then
begin
tm.wType := TIME_BYTES; //时间格式
WaveOutGetPosition(hwo, @tm, SizeOf(TMMTime));
Self.FPosition := tm.cb + GotoPosition; //当前播放位置
if SetDrawTime.Tag = 1 then
begin
Application.ProcessMessages;
SetDrawTime.Tag := 0;
if (Self.FDesBMP <> NIL) AND (Self.FDrawWave <> NIL) then
//画波形
{ HawDrawWave(lpWaveHdr.lpData, lpWaveHdr.dwBufferLength, Self.FDrawWave.Canvas,
Self.FDrawWave.ClientHeight, Self.FDrawWave.ClientWidth,
Self.WaveMin, Self.WaveMax, Self.FDrawLineColor, Self.FDrawBackColor,
LongInt(tm.cb) - LoadMemSize * (i - 1), Self.FDrawWave.ClientWidth * 2);
} SetDrawTime.Enabled := True;
Application.ProcessMessages;
end;
end;
end;
//释放音频信息头
WaveOutUnPrepareHeader(Self.hwo, lpWaveHdr, SizeOf(WAVEHDR));
if (Self.FNowPlay = CPSTOPING) OR (Self.FNowPlay = CPNONE) then
break;
end;
if lpWaveHdr <> NIL then
GlobalFreePtr(lpWaveHdr); //释放内存
Finally
SetDrawTime.Enabled := False;
SetDrawTime.Free;
Self.SetMemTime.Enabled := False;
if Self.FirstData <> NIL then
begin
GloBalFreePtr(Self.FirstData); //释放内存
Self.FirstData := NIL;
end;
if Self.NextData <> NIL then
begin
GloBalFreePtr(Self.NextData); //释放内存
Self.NextData := NIL;
end;
WaveOutClose(Self.hwo); //关闭音频设备
Self.GotoPosition := 0;
Self.pFormat.nSamplesPerSec := TempAV;
end;
end;
//***********************************************************************//
// //
// 分配内存、读取音频数据 //
// 参数: 无 //
// 返回值: 无 //
// //
//***********************************************************************//
procedure TCMPlayWave.SetPlayMem(Sender: TObject);
begin
Self.SetMemTime.Enabled := False;
//如果循环读取数据的总次数据大于要读取的次数
if Self.SetMemTime.Tag > Self.PlayLoop then
Exit;
Try //异常保护
if (Self.SetMemTime.Tag MOD 2) = 1 then
begin
if Self.FirstData <> NIL then
GloBalFreePtr(Self.FirstData); //释放内存
//是读取最后一段数据并且余下的数据长度不为 0
if (Self.MODInt <> 0) AND (Self.SetMemTime.Tag = Self.PlayLoop) then
Self.PlaySize := Self.MODInt //要播放的音频数据的长度
else
Self.PlaySize := LoadMemSize;
//开辟内存
Self.FirstData := PChar(GlobalAllocPtr(GMEM_MOVEABLE or GMEM_SHARE, Self.PlaySize));
if Self.FirstData = NIL then
raise EMMIO.Create('不能分配内存!');
if mmioRead(Self.hio, Self.FirstData, Self.PlaySize) <> Self.PlaySize then
raise EMMIO.Create('读取数据出错:可能文件已被破坏!');
end
else begin
if Self.NextData <> NIL then
GloBalFreePtr(Self.NextData);
if (Self.MODInt <> 0) AND (Self.SetMemTime.Tag = Self.PlayLoop) then
Self.PlaySize := Self.MODInt
else
Self.PlaySize := LoadMemSize;
Self.NextData := PChar(GlobalAllocPtr(GMEM_MOVEABLE or GMEM_SHARE, Self.PlaySize));
if Self.NextData = NIL then
raise EMMIO.Create('不能分配内存!');
//从文件中读取指定长度的音频数据
if mmioRead(Self.hio, Self.NextData, Self.PlaySize) <> Self.PlaySize then
raise EMMIO.Create('读取数据出错:可能文件已被破坏!');
end;
Finally;
end;
Self.SetMemTime.Tag := Self.SetMemTime.Tag + 1;
end;
//***********************************************************************//
// //
// 开始播放音频 //
// 参数: //
// Start : 开始位置 //
// Length : 要播放的音频数据的长度 //
// ( 0 表示播放所用的数据 ) //
// 返回值: 无 //
// //
//***********************************************************************//
procedure TCMPlayWave.Play(const Start, Length: DWORD);
begin
if Self.NowPlay <> CPSTOPING then //不是在停止状态下
Exit;
if (Start < Self.DataLength) and (Self.GotoPosition = 0) then
Self.GotoPosition := Start; //确定起始位置
Self.PlayDataLength := Length; //要播放的音频数据的长度
//确定要播放的音频数据的长度
if (Self.PlayDataLength = 0) or ((Self.GotoPosition + Length) > Self.DataLength) then
Self.PlayDataLength := Self.DataLength - Self.GotoPosition;
Self.PlayWave; //开始播放
Self.FNowPlay := CPSTOPING; //处于停止状态 ( 播放完毕 )
//发送播放完毕的消息
SendMessage(Application.MainForm.Handle, CM_PLAYEND, 0, 0);
end;
//***********************************************************************//
// //
// 暂停播放 //
// 参数: 无 //
// 返回值: 无 //
// //
//***********************************************************************//
procedure TCMPlayWave.Pause;
begin
if Self.NowPlay <> CPPLAYING then //不是在播放状态下
Exit;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -