index a5c83646ff18937ecf004e228250272b3af25b05..d3ee161d8c3e099aaa24ae52793b2078a93343be 100644 (file)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
e_SoundsArray: array of TSoundRec = nil;
e_ZeroPosition: array [0..2] of ALfloat = (0, 0, 0);
e_ALError: ALenum = 0;
e_SoundsArray: array of TSoundRec = nil;
e_ZeroPosition: array [0..2] of ALfloat = (0, 0, 0);
e_ALError: ALenum = 0;
+ e_SoundFont: string = '';
+ e_MusicLerp: Boolean = True;
implementation
uses
implementation
uses
- g_window, g_options, utils;
+ g_options, utils;
const
NUM_SOURCES = 255; // + 1 stereo
const
NUM_SOURCES = 255; // + 1 stereo
alStreamData: array [0..STREAM_BUFSIZE-1] of Byte;
alStreamAvail: Integer = NUM_STREAM_BUFFERS;
alStreamData: array [0..STREAM_BUFSIZE-1] of Byte;
alStreamAvail: Integer = NUM_STREAM_BUFFERS;
+{$IFNDEF OPENAL_SINGLETHREADED}
+var
+ StreamThread: TThreadID = NilThreadId;
+ StreamThreadRunning: Boolean = False;
+ StreamLock: TRTLCriticalSection;
+ StreamBufTime: Integer = 10; // time to sleep between buffer checks
+
+procedure UpdateStreamSource(Src: Integer); forward;
+
+function StreamThreadProc(Param: Pointer): PtrInt;
+begin
+ while StreamThreadRunning do
+ begin
+ EnterCriticalSection(StreamLock);
+ UpdateStreamSource(MUSIC_SOURCE);
+ LeaveCriticalSection(StreamLock);
+ Sleep(StreamBufTime);
+ end;
+ Result := 0;
+end;
+{$ENDIF}
+
function CheckALError(): Boolean;
begin
e_ALError := alGetError();
function CheckALError(): Boolean;
begin
e_ALError := alGetError();
else
alStreamAvail := NUM_STREAM_BUFFERS;
else
alStreamAvail := NUM_STREAM_BUFFERS;
+ {$IFNDEF OPENAL_SINGLETHREADED}
+ InitCriticalSection(StreamLock);
+ StreamThreadRunning := True;
+ StreamThread := BeginThread(Addr(StreamThreadProc));
+ {$ENDIF}
+
Result := True;
end;
Result := True;
end;
alGetSourcei(S, AL_SOURCE_STATE, Result);
end;
alGetSourcei(S, AL_SOURCE_STATE, Result);
end;
+function LoadEntireSound(var Snd: TSoundRec; Loader: TSoundLoader): Boolean;
+var
+ Frame: Pointer;
+ Data: Pointer;
+ Rx: LongWord;
+ DataLen, OldLen: LongWord;
+const
+ CHUNK_SIZE = 65536 * 2 * 2;
+begin
+ Result := False;
+
+ Frame := GetMem(CHUNK_SIZE);
+ if Frame = nil then exit;
+
+ Data := nil;
+ DataLen := 0;
+
+ repeat
+ Rx := Loader.FillBuffer(Frame, CHUNK_SIZE);
+ if Rx = 0 then break;
+
+ OldLen := DataLen;
+ DataLen := DataLen + Rx;
+ Data := ReAllocMem(Data, DataLen);
+ if Data = nil then begin FreeMem(Frame); exit; end;
+
+ Move(Frame^, (Data + OldLen)^, Rx);
+ until Loader.Finished();
+
+ FreeMem(Frame);
+
+ alGenBuffers(1, Addr(Snd.alBuffer));
+ if CheckALError() then
+ begin
+ e_LogWritefln('AL: Could not create AL buffer: %s', [GetALError()]);
+ FreeMem(Data);
+ exit;
+ end;
+
+ alBufferData(
+ Snd.alBuffer,
+ GetALSoundFormat(Loader.Format),
+ Data,
+ DataLen,
+ Loader.Format.SampleRate
+ );
+
+ FreeMem(Data);
+
+ if CheckALError() then
+ begin
+ e_LogWriteln('AL: Could not fill AL buffer: ' + GetALError());
+ alDeleteBuffers(1, Addr(Snd.alBuffer));
+ Snd.alBuffer := 0;
+ exit;
+ end;
+
+ Result := True;
+end;
+
function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
var
find_id: DWORD;
Loader: TSoundLoader;
function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
var
find_id: DWORD;
Loader: TSoundLoader;
- OutData: Pointer;
- OutLen: LongWord;
begin
ID := NO_SOUND_ID;
Result := False;
begin
ID := NO_SOUND_ID;
Result := False;
exit;
end;
exit;
end;
- Loader.Looping := e_SoundsArray[find_id].Loops;
-
- if not Loader.Load(FileName, e_SoundsArray[find_id].isMusic) then
+ if not Loader.Load(FileName, e_SoundsArray[find_id].Loops) then
begin
e_LogWritefln('Could not load sound `%s`', [FileName]);
exit;
begin
e_LogWritefln('Could not load sound `%s`', [FileName]);
exit;
alGetError(); // reset error state, god damn it
alGetError(); // reset error state, god damn it
- if not Loader.Streaming then
+ if not isMusic then
begin
begin
- alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
- if CheckALError() then
- begin
- e_LogWritefln('Could not create AL buffer for `%s`: %s', [FileName, GetALError()]);
- Loader.Free();
- exit;
- end;
-
- OutLen := Loader.GetAll(OutData);
- alBufferData(
- e_SoundsArray[find_id].alBuffer,
- GetALSoundFormat(Loader.Format),
- OutData,
- OutLen,
- Loader.Format.SampleRate
- );
-
+ if not LoadEntireSound(e_SoundsArray[find_id], Loader) then
+ e_LogWritefln('AL: Could not buffer sound effect `%s`', [FileName]);
// don't need this anymore
Loader.Free();
Loader := nil;
// don't need this anymore
Loader.Free();
Loader := nil;
-
- if CheckALError() then
- begin
- e_LogWriteln('AL: what the fuck: ' + GetALError());
- alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
- e_SoundsArray[find_id].alBuffer := 0;
- exit;
- end;
end
else
begin
end
else
begin
@@ -343,8 +405,6 @@ function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic:
var
find_id: DWORD;
Loader: TSoundLoader;
var
find_id: DWORD;
Loader: TSoundLoader;
- OutData: Pointer;
- OutLen: LongWord;
begin
ID := NO_SOUND_ID;
Result := False;
begin
ID := NO_SOUND_ID;
Result := False;
exit;
end;
exit;
end;
- Loader.Looping := e_SoundsArray[find_id].Loops;
-
- if not Loader.Load(pData, LongWord(Length), e_SoundsArray[find_id].isMusic) then
+ if not Loader.Load(pData, LongWord(Length), e_SoundsArray[find_id].Loops) then
begin
e_LogWritefln('Could not load sound `%p`', [pData]);
exit;
begin
e_LogWritefln('Could not load sound `%p`', [pData]);
exit;
alGetError(); // reset error state, god damn it
alGetError(); // reset error state, god damn it
- if not Loader.Streaming then
+ if not isMusic then
begin
begin
- alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
- if CheckALError() then
- begin
- e_LogWritefln('Could not create AL buffer for `%p`: %s', [pData, GetALError()]);
- Loader.Free();
- exit;
- end;
-
- OutLen := Loader.GetAll(OutData);
- alBufferData(
- e_SoundsArray[find_id].alBuffer,
- GetALSoundFormat(Loader.Format),
- OutData,
- OutLen,
- Loader.Format.SampleRate
- );
-
+ if not LoadEntireSound(e_SoundsArray[find_id], Loader) then
+ e_LogWritefln('AL: Could not buffer sound effect `%p`', [pData]);
// don't need this anymore
Loader.Free();
Loader := nil;
// don't need this anymore
Loader.Free();
Loader := nil;
-
- if CheckALError() then
- begin
- e_LogWriteln('AL: what the fuck: ' + GetALError());
- alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
- e_SoundsArray[find_id].alBuffer := 0;
- exit;
- end;
end
else
begin
end
else
begin
e_SoundsArray[find_id].Loader := Loader;
end;
e_SoundsArray[find_id].Loader := Loader;
end;
+ // the calling side won't free this, the loader will get a copy, so fuck it
+ FreeMem(pData);
ID := find_id;
Result := True;
end;
ID := find_id;
Result := True;
end;
end;
procedure AssignSound(ID: DWORD; Src: ALuint); inline;
end;
procedure AssignSound(ID: DWORD; Src: ALuint); inline;
+var
+ S: ALint;
begin
alGetError(); // reset error state
if e_SoundsArray[ID].Loader <> nil then
begin
// this is a stream
begin
alGetError(); // reset error state
if e_SoundsArray[ID].Loader <> nil then
begin
// this is a stream
+ {$IFNDEF OPENAL_SINGLETHREADED}
+ // lock the stream so the stream thread doesn't shit itself
+ EnterCriticalSection(StreamLock);
+ // number of stereo samples / samplerate =
+ // time until buffer runs out
+ StreamBufTime :=
+ (STREAM_BUFSIZE div (2 * e_SoundsArray[ID].Loader.Format.SampleBits div 8)) div
+ (e_SoundsArray[ID].Loader.Format.SampleRate div 1000) - 1;
+ if StreamBufTime < 1 then StreamBufTime := 1;
+ {$ENDIF}
// reset position
// reset position
- e_SoundsArray[ID].Loader.SetPosition(0);
- if CurStream <> ID then // changing streams, stop the thing just in case
- alSourceStop(Src);
+ e_SoundsArray[ID].Loader.Restart();
+ if CurStream <> ID then // changing streams
+ begin
+ alSourceStop(Src); // this should mark all buffers as processed
+ alGetSourcei(Src, AL_BUFFERS_PROCESSED, S);
+ // unqueue all buffers
+ if S > 0 then
+ begin
+ alSourceUnqueueBuffers(Src, S, @alStreamBufs[alStreamAvail]);
+ alStreamAvail := NUM_STREAM_BUFFERS;
+ end;
+ end;
// this shit is playing now
CurStream := ID;
// this shit is playing now
CurStream := ID;
+ {$IFNDEF OPENAL_SINGLETHREADED}
+ // unlock the stream
+ LeaveCriticalSection(StreamLock);
+ {$ENDIF}
end
else
begin
end
else
begin
procedure e_ReleaseSoundSystem();
begin
procedure e_ReleaseSoundSystem();
begin
+ {$IFNDEF OPENAL_SINGLETHREADED}
+ if StreamThread <> NilThreadId then
+ begin
+ StreamThreadRunning := False;
+ WaitForThreadTerminate(StreamThread, 66666);
+ StreamThread := NilThreadId;
+ DoneCriticalSection(StreamLock);
+ end;
+ {$ENDIF}
+
e_RemoveAllSounds();
alcMakeContextCurrent(nil);
e_RemoveAllSounds();
alcMakeContextCurrent(nil);
alOwners[S] := nil;
end;
alOwners[S] := nil;
end;
+ {$IFDEF OPENAL_SINGLETHREADED}
// update the stream sources
UpdateStreamSource(MUSIC_SOURCE);
// update the stream sources
UpdateStreamSource(MUSIC_SOURCE);
+ {$ENDIF}
end;
{ TBasicSound: }
end;
{ TBasicSound: }
function TBasicSound.GetPan(): Single;
var
function TBasicSound.GetPan(): Single;
var
- Pos: array [0..2] of ALfloat;
+ Pos: array [0..2] of ALfloat = (0, 0, 0);
begin
Result := 0.0;
if InvalidSource() then
begin
Result := 0.0;
if InvalidSource() then