通過 IDirectSoundBuffer 的 SetVolume、SetPan、SetFrequency、SetCurrentPosition 方法可以簡單進行這些設置.
同時 IDirectSoundBuffer 也有對應的 GetVolume、GetPan、GetFrequency、GetCurrentPosition 方法.
關鍵的一點是如果能讓緩沖區接受音量、相位和頻率的設置, 必須在建立緩沖區時指定相應的標志.
下面的常量說明了它們的取值范圍:
DSBVOLUME_MAX = 0; //音量最大值, 保持在控制面板設置的音量
DSBVOLUME_MIN = -10000; //音量最小值
DSBPAN_LEFT = -10000; //左聲道
DSBPAN_CENTER = 0; //均衡
DSBPAN_RIGHT = 10000; //右聲道
DSBFREQUENCY_ORIGINAL = 0; //使用默認
DSBFREQUENCY_MIN = 100; //頻率最小值
DSBFREQUENCY_MAX = 200000; //頻率最大值, 在 DirectSound 9.0 之下的版本, 此值是 100000
在上一個例子中, 最占篇幅的就是那兩個函數; 為了更方便使用, 把它們做在了一個 TReadWaveFile 類裡:
{實現 TReadWaveFile 類的單元}
unit ReadWaveFile;
interface
uses Windows, Classes, SysUtils, MMSystem;
type
TReadWaveFile = class
private
FFileHandle: HMMIO;
FFormat: TWaveFormatEx;
FSize: DWord;
public
destructor Destroy; override;
function Open(FileName: string): Boolean; //打開文件並讀取信息
function Read(pDest: Pointer; Size: DWord): Boolean; //讀出波形數據
property Format: TWaveFormatEx read FFormat; //讀出格式數據
property Size: DWord read FSize; //讀出波形數據的大小
end;
implementation
{ TReadWaveFile }
destructor TReadWaveFile.Destroy;
begin
if FFileHandle > 0 then mmioClose(FFileHandle, 0);
inherited;
end;
function TReadWaveFile.Open(FileName: string): Boolean;
var
ckiRIFF,ckiFmt,ckiData: TMMCKInfo;
begin
Result := False;
if not FileExists(FileName) then Exit;
FFileHandle := mmioOpen(PChar(FileName), nil, MMIO_READ);
if FFileHandle = 0 then Exit;
ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));
mmioDescend(FFileHandle, @ckiRIFF, nil, MMIO_FINDRIFF);
if (ckiRIFF.ckid <> FOURCC_RIFF) or (ckiRIFF.fccType <> mmiOStringToFOURCC('WAVE',0)) then Exit;
ZeroMemory(@FFormat, SizeOf(TWaveFormatEx));
ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));
ckiFmt.ckid := mmiOStringToFOURCC('fmt', 0);
ZeroMemory(@ckiData, SizeOf(TMMCKInfo));
ckiData.ckid := mmiOStringToFOURCC('data', 0);
if (mmioDescend(FFileHandle, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then
mmioRead(FFileHandle, @FFormat, ckiFmt.cksize);
if (mmioDescend(FFileHandle, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then
FSize := ckiData.cksize;
Result := True;
end;
function TReadWaveFile.Read(pDest: Pointer; Size: DWord): Boolean;
begin
Result := mmioRead(FFileHandle, pDest, Size) = Size;
end;
end.
測試程序用到了四個 Button 和三個 TrackBar 還有它們的默認事件:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
Button1: TButton; //打開並播放
Button2: TButton; //反復播放
Button3: TButton; //暫停
Button4: TButton; //從頭播放
TrackBar1: TTrackBar; //用於音量調節
TrackBar2: TTrackBar; //用於相位調節
TrackBar3: TTrackBar; //用於頻率調節
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure TrackBar1Change(Sender: TObject);
procedure TrackBar2Change(Sender: TObject);
procedure TrackBar3Change(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses DirectSound, MMSystem, ReadWaveFile; // ReadWaveFile 是 TReadWaveFile 類所在的單元
var
myDSound: IDirectSound8;
buf: IDirectSoundBuffer;
procedure TForm1.FormCreate(Sender: TObject);
begin
TrackBar1.Min := DSBVOLUME_MIN;
TrackBar1.Max := DSBVOLUME_MAX;
TrackBar2.Min := DSBPAN_LEFT;
TrackBar2.Max := DSBPAN_RIGHT;
TrackBar3.Min := 100;
TrackBar3.Max := 100000;
Button1.Caption := '打開並播放';
Button2.Caption := '反復播放';
Button3.Caption := '暫停';
Button4.Caption := '從頭播放';
end;
procedure TForm1.Button1Click(Sender: TObject);
var
bufDesc: TDSBufferDesc;
p1: Pointer;
n1: DWord;
wavPath: string;
wav: TReadWaveFile; //
begin
buf := nil;
myDSound := nil;
with TOpenDialog.Create(Self) do begin
Filter := 'Wave File(*.wav)|*.wav';
if Execute then wavPath := FileName;
Free;
end;
wav := TReadWaveFile.Create;
wav.Open(wavPath);
if (wav.Format.wFormatTag <> WAVE_FORMAT_PCM) then
begin
ShowMessage('只能是 PCM 格式的 WAVE 文件');
Exit;
end;
DirectSoundCreate8(nil, myDSound, nil);
myDSound.SetCoOperativeLevel(Self.Handle, DSSCL_PRIORITY);
ZeroMemory(@bufDesc, SizeOf(TDSBufferDesc));
bufDesc.dwSize := SizeOf(TDSBufferDesc);
{指定緩沖區允許音量、相位和頻率調節}
bufDesc.dwFlags := DSBCAPS_STATIC or DSBCAPS_CTRLVOLUME or DSBCAPS_CTRLPAN or DSBCAPS_CTRLFREQUENCY;
bufDesc.dwBufferBytes := wav.Size;
bufDesc.lpwfxFormat := @wav.Format;
myDSound.CreateSoundBuffer(bufDesc, buf, nil);
buf.Lock(0, 0, @p1, @n1, nil, nil, DSBLOCK_ENTIREBUFFER);
wav.Read(p1, n1);
buf.Unlock(p1, n1, nil, 0);
buf.Play(0, 0, 0);
TrackBar1.Position := 0;
TrackBar2.Position := 0;
TrackBar3.Position := wav.Format.nSamplesPerSec;
wav.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if buf <> nil then buf.Play(0, 0, DSBPLAY_LOOPING);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
if buf <> nil then buf.Stop;
end;
{從頭播放}
procedure TForm1.Button4Click(Sender: TObject);
begin
if buf = nil then Exit;
buf.Stop;
buf.SetCurrentPosition(0);
buf.Play(0, 0, 0);
end;
{音量調節}
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
if buf <> nil then buf.SetVolume(TTrackBar(Sender).Position);
end;
{相位調節}
procedure TForm1.TrackBar2Change(Sender: TObject);
begin
if buf <> nil then buf.SetPan(TTrackBar(Sender).Position);
end;
{頻率調節}
procedure TForm1.TrackBar3Change(Sender: TObject);
begin
if buf <> nil then buf.SetFrequency(TTrackBar(Sender).Position);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
buf := nil;
myDSound := nil;
end;
end.