unit LiveAudioMixerPlayer; interface uses Windows, Classes, WaveUtils, WaveStorage, WaveOut, MMSystem; type TLiveAudioMixerPlayer = class(TWaveAudioOut) private FPCMFormat: TPCMFormat; FTrack1Volume: Byte; FTrack2Volume: Byte; FWave, FWave1, FWave2: TWave; FTrack1: TWaveStreamAdapter; FTrack2: TWaveStreamAdapter; FTrack2Paused: Boolean; FTrack1Paused: Boolean; procedure SetPCMFormat(const Value: TPCMFormat); function GetTrack1Position: Cardinal; function GetTrack2Position: Cardinal; procedure SetTrack1Position(const Value: Cardinal); procedure SetTrack2Position(const Value: Cardinal); protected procedure GetWaveFormat(var pWaveFormat: PWaveFormatEx; var FreeIt: Boolean); override; function GetWaveData(const Buffer: Pointer; BufferSize: DWORD; var NumLoops: DWORD): DWORD; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Paused; property Active; published property PCMFormat: TPCMFormat read FPCMFormat write SetPCMFormat default Stereo16bit8000Hz; property Options; property Volume; // Percent (Both channels) property VolumeLeft; // Percent (Left channel) property VolumeRight; // Percent (Right channel) property Track1: TWaveStreamAdapter read FTrack1 write FTrack1; property Track1Volume: Byte read FTrack1Volume write FTrack1Volume default 100; // Percent property Track1Position: Cardinal read GetTrack1Position write SetTrack1Position; property Track1Paused: Boolean read FTrack1Paused write FTrack1Paused; property Track2: TWaveStreamAdapter read FTrack2 write FTrack2; property Track2Volume: Byte read FTrack2Volume write FTrack2Volume default 100; // Percent property Track2Position: Cardinal read GetTrack2Position write SetTrack2Position; property Track2Paused: Boolean read FTrack2Paused write FTrack2Paused; property BufferLength; // Milliseconds property BufferCount; property Async; property OnActivate; property OnDeactivate; property OnPause; property OnResume; property OnError; property OnLevel; end; implementation { TLiveAudioMixerPlayer } constructor TLiveAudioMixerPlayer.Create(AOwner: TComponent); begin inherited Create(AOwner); FWave := TWave.Create; FWave1 := TWave.Create; FWave2 := TWave.Create; FPCMFormat := Stereo16bit8000Hz; FTrack1Volume := 100; FTrack2Volume := 100; FTrack1Paused := False; FTrack2Paused := False; BufferLength := 10; BufferCount := 10; end; destructor TLiveAudioMixerPlayer.Destroy; begin FWave2.Free; FWave1.Free; FWave.Free; inherited; end; function TLiveAudioMixerPlayer.GetTrack1Position: Cardinal; begin Result := 0; if Assigned(FTrack1) then if FTrack1.Valid and not FTrack1.Empty then Result := Round(FTrack1.Length * FTrack1.Stream.Position / FTrack1.Stream.Size); end; function TLiveAudioMixerPlayer.GetTrack2Position: Cardinal; begin Result := 0; if Assigned(FTrack2) then if FTrack2.Valid and not FTrack2.Empty then Result := Round(FTrack2.Length * FTrack2.Stream.Position / FTrack2.Stream.Size); end; function TLiveAudioMixerPlayer.GetWaveData(const Buffer: Pointer; BufferSize: DWORD; var NumLoops: DWORD): DWORD; var BufferT: Pointer; Count: DWORD; begin GetMem(BufferT, BufferSize); if Assigned(FTrack1) then if FTrack1.Valid and not FTrack1.Empty and not FTrack1Paused then begin if FTrack1.State <> wssReading then FTrack1.BeginRead; Count := FTrack1.Read(BufferT^, BufferSize); if Count > 0 then begin FWave1.BeginRewrite(FTrack1.WaveFormat); FWave1.Write(BufferT^, Count); FWave1.EndRewrite; FWave1.ChangeVolume(-100 + FTrack1Volume); FWave1.ConvertToPCM(FPCMFormat); end; end; if Assigned(FTrack2) then if FTrack2.Valid and not FTrack2.Empty and not FTrack2Paused then begin if FTrack2.State <> wssReading then FTrack2.BeginRead; Count := FTrack2.Read(BufferT^, BufferSize); if Count > 0 then begin FWave2.BeginRewrite(FTrack2.WaveFormat); FWave2.Write(BufferT^, Count); FWave2.EndRewrite; FWave2.ChangeVolume(-100 + FTrack2Volume); FWave2.ConvertToPCM(FPCMFormat); end; end; FreeMem(BufferT); Result := 0; if not FWave1.Empty and not FWave2.Empty then begin FWave.Mix([FWave1, FWave2]); FWave.BeginRead; Result := FWave.Read(Buffer^, BufferSize); FWave.EndRead; end else if not FWave1.Empty then begin FWave1.BeginRead; Result := FWave1.Read(Buffer^, BufferSize); FWave1.EndRead; end else if not FWave2.Empty then begin FWave2.BeginRead; Result := FWave2.Read(Buffer^, BufferSize); FWave2.EndRead; end; FWave1.Clear; FWave2.Clear; end; procedure TLiveAudioMixerPlayer.GetWaveFormat(var pWaveFormat: PWaveFormatEx; var FreeIt: Boolean); begin if PCMFormat <> nonePCM then begin GetMem(pWaveFormat, SizeOf(TWaveFormatEx)); SetPCMAudioFormatS(pWaveFormat, PCMFormat) end; end; procedure TLiveAudioMixerPlayer.SetPCMFormat(const Value: TPCMFormat); begin if PCMFormat <> Value then begin if Active then raise EWaveAudioInvalidOperation.Create('Audio format cannot be changed while device is open') else fPCMFormat := Value; end; end; procedure TLiveAudioMixerPlayer.SetTrack1Position(const Value: Cardinal); begin if Assigned(FTrack1) then if FTrack1.Valid and not FTrack1.Empty then begin if FTrack1.State <> wssReading then FTrack1.BeginRead; FTrack1.Position := GetWaveDataPositionOffset(FTrack1.WaveFormat, Value); //FTrack1.Stream.Position := Round(FTrack1.Stream.Size * Value / FTrack1.Length); end; end; procedure TLiveAudioMixerPlayer.SetTrack2Position(const Value: Cardinal); begin if Assigned(FTrack2) then if FTrack2.Valid and not FTrack2.Empty then begin if FTrack2.State <> wssReading then FTrack2.BeginRead; FTrack2.Position := GetWaveDataPositionOffset(FTrack2.WaveFormat, Value); //FTrack2.Stream.Position := Round(FTrack2.Stream.Size * Value / FTrack2.Length); end; end; end.