[pascal]unit mp3stream;

interface

uses
SysUtils, Windows, Classes, DirectSound, MMSystem, mpg123;


type
TMP3 = class(TThread)
public
FFileName: string;
Fhandle: Pmpg123_handle;
Frate: Integer;
Fchannels: Integer;
Fencoding: Integer;
Fbuffersize: Cardinal;

FPlaying: Boolean;
public
procedure updatebuffer;
procedure prebuffer;
procedure Play;
procedure Stop;
procedure Execute; override;
public
function initmpg123: Boolean;
constructor Create(const filepath: string);
destructor Destroy; override;
end;

implementation

uses
main;

{ TMP3 }

constructor TMP3.Create(const filepath: string);
begin
inherited Create(True);
FFileName := filepath;
Priority := tpTimeCritical;
if FileExists(FFileName) then
initmpg123;
end;

destructor TMP3.Destroy;
begin
mpg123_close(Fhandle);
mpg123_exit;
inherited;
end;

procedure TMP3.Execute;
var
critical : RTL_CRITICAL_SECTION;
begin
InitializeCriticalSection(critical);
EnterCriticalSection(critical);
repeat
updatebuffer;
Sleep(300);
until Terminated;
LeaveCriticalSection(critical);
DeleteCriticalSection(critical);
end;

function TMP3.initmpg123: Boolean;
begin
Result := False;
if mpg123_init <> 0 then Exit;

Fhandle := mpg123_new('MMX', nil); // MMX is common
if Fhandle = nil then Exit;
mpg123_open(Fhandle, PChar(FFileName));
mpg123_getformat(Fhandle, @Frate, @Fchannels, @Fencoding);
mpg123_format_none(Fhandle);
mpg123_format(Fhandle, Frate, Fchannels, Fencoding);
end;

procedure TMP3.Play;
begin
prebuffer;
resume;
sbuffer.Play(0, 0, DSBPLAY_LOOPING);
end;

procedure TMP3.Stop;
begin
Suspend;
sbuffer.Stop;
end;

procedure TMP3.prebuffer;
var
buffer: PByte;
size: Cardinal;
d: Cardinal;
begin
Fbuffersize := wfm.nAvgBytesPerSec;

sbuffer.Lock(0, Fbuffersize, @buffer, @size, nil, nil, 0);

mpg123_read(Fhandle, buffer, size, @d);

sbuffer.Unlock(buffer, size, nil, 0);
end;

procedure TMP3.updatebuffer;
var
buffer: PByte;
size: Cardinal;
d: Cardinal;
e : Cardinal;
section : Cardinal;
begin
e := WaitForMultipleObjects(2,@events,False,0);

if e = 0 then
section := 1
else
if e = 1 then
section := 0
else
exit;



sbuffer.Lock(section*Fbuffersize,Fbuffersize,@buff er,@size,nil,nil,0);

mpg123_read(Fhandle, buffer, size, @d);

sbuffer.Unlock(buffer,size,nil,0);
end;

end.
[/pascal]
[pascal]unit main;

interface

uses
SysUtils,
Windows,
DirectSound,
MMSystem,
Classes,
mp3stream;

var
DS: IDirectSound8;
dsnotify : IDirectSoundNotify8;
events : array [0..1] of Cardinal;
notifs : array [0..1] of TDSBPOSITIONNOTIFY;

sbuffer: IDirectSoundBuffer;

wfm: TWAVEFORMATEX;
desc: TDSBUFFERDESC;

mp3: TMP3;

procedure RUN;

implementation

procedure RUN;
begin
IsMultiThread := True;
SetConsoleTitle('APP');

mp3 := TMP3.Create('mp3.mp3');

if DirectSoundCreate8(nil, DS, nil) <> DS_OK then Exit;

if DS.SetCooperativeLevel(FindWindow(nil, 'APP'), DSSCL_NORMAL) <> DS_OK then Exit; ;

FillChar(wfm, SizeOf(wfm), 0);
wfm.cbSize := SizeOf(wfm);
wfm.nChannels := mp3.Fchannels;
wfm.wBitsPerSample := 16;
wfm.nSamplesPerSec := mp3.Frate;
wfm.nAvgBytesPerSec := wfm.nSamplesPerSec * wfm.nChannels * 2;
wfm.nBlockAlign := 2 * wfm.nChannels;
wfm.wFormatTag := WAVE_FORMAT_PCM;

// set up the buffer
desc.dwSize := SizeOf(desc);
desc.dwFlags := 0;
desc.lpwfxFormat := @wfm;
desc.dwReserved := 0;
desc.dwBufferBytes := wfm.nAvgBytesPerSec * 2;

desc.dwFlags :=
DSBCAPS_STATIC or
DSBCAPS_CTRLPOSITIONNOTIFY or
DSBCAPS_CTRLVOLUME or
DSBCAPS_GLOBALFOCUS ;

ds.CreateSoundBuffer(desc,sbuffer,nil);
sbuffer.QueryInterface(IID_IDirectSoundNotify8,dsn otify);

events[0] := CreateEvent(nil,False,False,'0.25 played');;
events[1] := CreateEvent(nil,False,False,'0.75 played');
notifs[0].hEventNotify := events[0];
notifs[0].dwOffset := wfm.nAvgBytesPerSec div 2;
notifs[1].hEventNotify := events[1];
notifs[1].dwOffset := (wfm.nAvgBytesPerSec * 3) div 2;

if dsnotify.SetNotificationPositions(2,@notifs) <> DS_OK then Exit;

mp3.Play;


sleep(1000 * 60 * 1);


mp3.Stop;

CloseHandle(events[0]);
CloseHandle(events[1]);

dsnotify := nil;
sbuffer := nil;
DS := nil;

mp3.Free;
end;

end.
[/pascal]