DEADSOFTWARE

Sound: OpenAL: Free the sound buffer after load
[d2df-sdl.git] / src / engine / e_sound_al.inc
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 interface
18 uses
19 AL,
20 {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
21 e_soundfile,
22 e_log,
23 SysUtils;
25 type
26 TSoundRec = record
27 Loader: TSoundLoader;
28 alBuffer: ALuint;
29 isMusic: Boolean;
30 Loops: Boolean;
31 nRefs: Integer;
32 end;
34 TBasicSound = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
35 private
36 FSource: Integer;
37 FOldGain: ALfloat;
38 FMuted: Boolean;
40 function InvalidSource(): Boolean; inline;
42 protected
43 FID: DWORD;
44 FMusic: Boolean;
45 FPosition: DWORD;
47 function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
49 public
50 constructor Create();
51 destructor Destroy(); override;
52 procedure SetID(ID: DWORD);
53 procedure FreeSound();
54 function IsPlaying(): Boolean;
55 procedure Stop();
56 function IsPaused(): Boolean;
57 procedure Pause(Enable: Boolean);
58 function GetVolume(): Single;
59 procedure SetVolume(Volume: Single);
60 function GetPan(): Single;
61 procedure SetPan(Pan: Single);
62 function IsMuted(): Boolean;
63 procedure Mute(Enable: Boolean);
64 function GetPosition(): DWORD;
65 procedure SetPosition(aPos: DWORD);
66 procedure SetPriority(priority: Integer);
67 end;
69 const
70 NO_SOUND_ID = DWORD(-1);
72 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
74 function e_LoadSound(FileName: string; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
75 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
77 function e_PlaySound(ID: DWORD): Integer;
78 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
79 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
80 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
82 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
83 procedure e_MuteChannels(Enable: Boolean);
84 procedure e_StopChannels();
86 procedure e_DeleteSound(ID: DWORD);
87 procedure e_RemoveAllSounds();
88 procedure e_ReleaseSoundSystem();
89 procedure e_SoundUpdate();
91 var
92 e_SoundFormat: TSoundFormat; // desired sound format
93 e_SoundsArray: array of TSoundRec = nil;
94 e_ZeroPosition: array [0..2] of ALfloat = (0, 0, 0);
95 e_ALError: ALenum = 0;
96 e_SoundFont: string = '';
97 e_MusicLerp: Boolean = True;
99 implementation
101 uses
102 g_window, g_options, utils;
104 const
105 NUM_SOURCES = 255; // + 1 stereo
106 NUM_STREAM_BUFFERS = 8;
107 STREAM_BUFSIZE = 8192;
108 MUSIC_SOURCE = 0;
110 var
111 SoundMuted: Boolean = False;
112 CurStream: DWORD = NO_SOUND_ID;
113 alDevice: PALCdevice = nil;
114 alContext: PALCcontext = nil;
115 // sources for everything
116 alSources: array [0..NUM_SOURCES] of ALuint;
117 // last TBasicSound that has played on each source
118 alOwners: array [0..NUM_SOURCES] of TBasicSound;
119 // buffers for the music stream
120 alStreamBufs: array [0..NUM_STREAM_BUFFERS-1] of ALuint;
121 alStreamData: array [0..STREAM_BUFSIZE-1] of Byte;
122 alStreamAvail: Integer = NUM_STREAM_BUFFERS;
124 function CheckALError(): Boolean;
125 begin
126 e_ALError := alGetError();
127 Result := e_ALError <> AL_NO_ERROR;
128 end;
130 function GetALError(): string;
131 begin
132 Result := '';
133 case e_ALError of
134 AL_NO_ERROR: Result := '';
135 AL_INVALID_NAME: Result := 'AL_INVALID_NAME';
136 AL_INVALID_ENUM: Result := 'AL_INVALID_ENUM';
137 AL_INVALID_VALUE: Result := 'AL_INVALID_VALUE';
138 AL_INVALID_OPERATION: Result := 'AL_INVALID_OPERATION';
139 AL_OUT_OF_MEMORY: Result := 'AL_OUT_OF_MEMORY';
140 else Result := Format('unknown error %x', [e_ALError]);
141 end;
142 end;
144 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
145 var
146 alExt, alRend, alVendor, alVer: string;
147 WantDev: string = '';
148 WantAttrs: array [0..4] of ALCint = (
149 ALC_STEREO_SOURCES, 1,
150 ALC_MONO_SOURCES, NUM_SOURCES,
152 );
153 begin
154 Result := False;
156 WantDev := alcGetString(nil, ALC_DEVICE_SPECIFIER);
157 e_LogWritefln('AL: available devices: %s', [WantDev]);
159 // TODO: open a dummy device when NoOutput is true or something
160 WantDev := alcGetString(nil, ALC_DEFAULT_DEVICE_SPECIFIER);
161 e_LogWritefln('AL: trying to open device %s', [WantDev]);
163 alDevice := alcOpenDevice(PChar(WantDev));
164 if alDevice = nil then
165 begin
166 e_LogWritefln('AL: ERROR: could not open device %s: %s', [WantDev, GetALError()]);
167 exit;
168 end;
170 alContext := alcCreateContext(alDevice, WantAttrs);
171 if alContext = nil then
172 begin
173 e_LogWritefln('AL: ERROR: could not create context: %s', [GetALError()]);
174 alcCloseDevice(alDevice);
175 alDevice := nil;
176 exit;
177 end;
179 alcMakeContextCurrent(alContext);
181 // TODO: actually parse these from alc attributes or something
182 e_SoundFormat.SampleRate := 48000;
183 e_SoundFormat.SampleBits := 16;
184 e_SoundFormat.Channels := 2;
186 alVendor := alGetString(AL_VENDOR);
187 alRend := alGetString(AL_RENDERER);
188 alVer := alGetString(AL_VERSION);
189 alExt := alGetString(AL_EXTENSIONS);
191 e_LogWriteln('AL INFO:');
192 e_LogWriteln(' Version: ' + alVer);
193 e_LogWriteln(' Vendor: ' + alVendor);
194 e_LogWriteln(' Renderer: ' + alRend);
195 e_LogWriteln(' Device: ' + WantDev);
196 e_LogWriteln(' Sample rate: ' + IntToStr(e_SoundFormat.SampleRate));
197 e_LogWriteln(' Extensions:');
198 e_LogWriteln(' ' + alExt);
200 ZeroMemory(@alSources[0], sizeof(alSources));
201 ZeroMemory(@alOwners[0], sizeof(alOwners));
202 ZeroMemory(@alStreamBufs[0], sizeof(alStreamBufs));
203 ZeroMemory(@alStreamData[0], sizeof(alStreamData));
204 CurStream := NO_SOUND_ID;
206 alGetError(); // reset the goddamn error state
207 alGenSources(1, @alSources[0]); // generate the music source
208 if CheckALError() then
209 e_LogWriteln('AL: ERROR: alGenSources() for music failed: ' + GetALError());
211 alStreamAvail := 0;
212 alGenBuffers(NUM_STREAM_BUFFERS, @alStreamBufs[0]); // generate buffers for the music stream
213 if CheckALError() then
214 e_LogWriteln('AL: ERROR: alGenSources() for music failed: ' + GetALError())
215 else
216 alStreamAvail := NUM_STREAM_BUFFERS;
218 Result := True;
219 end;
221 function FindESound(): DWORD;
222 var
223 i: Integer;
225 begin
226 if e_SoundsArray <> nil then
227 for i := 0 to High(e_SoundsArray) do
228 if (e_SoundsArray[i].alBuffer = 0) and (e_SoundsArray[i].Loader = nil) then
229 begin
230 Result := i;
231 Exit;
232 end;
234 if e_SoundsArray = nil then
235 begin
236 SetLength(e_SoundsArray, 16);
237 Result := 0;
238 end
239 else
240 begin
241 Result := High(e_SoundsArray) + 1;
242 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
243 end;
244 end;
246 function GetALSoundFormat(Fmt: TSoundFormat): ALenum; inline;
247 begin
248 if Fmt.Channels = 2 then
249 begin
250 if Fmt.SampleBits = 16 then
251 Result := AL_FORMAT_STEREO16
252 else
253 Result := AL_FORMAT_STEREO8;
254 end
255 else
256 begin
257 if Fmt.SampleBits = 16 then
258 Result := AL_FORMAT_MONO16
259 else
260 Result := AL_FORMAT_MONO8;
261 end;
262 end;
264 function GetALSourceState(S: ALuint): ALint; inline;
265 begin
266 alGetSourcei(S, AL_SOURCE_STATE, Result);
267 end;
269 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
270 var
271 find_id: DWORD;
272 Loader: TSoundLoader;
273 OutData: Pointer;
274 OutLen: LongWord;
275 begin
276 ID := NO_SOUND_ID;
277 Result := False;
279 find_id := FindESound();
281 e_SoundsArray[find_id].Loader := nil;
282 e_SoundsArray[find_id].isMusic := isMusic;
283 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
284 e_SoundsArray[find_id].nRefs := 0;
286 Loader := e_GetSoundLoader(FileName);
287 if Loader = nil then
288 begin
289 e_LogWritefln('Could not find loader for sound `%s`', [FileName]);
290 exit;
291 end;
293 Loader.Looping := e_SoundsArray[find_id].Loops;
295 if not Loader.Load(FileName, e_SoundsArray[find_id].isMusic) then
296 begin
297 e_LogWritefln('Could not load sound `%s`', [FileName]);
298 exit;
299 end;
301 alGetError(); // reset error state, god damn it
303 if not Loader.Streaming then
304 begin
305 alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
306 if CheckALError() then
307 begin
308 e_LogWritefln('Could not create AL buffer for `%s`: %s', [FileName, GetALError()]);
309 Loader.Free();
310 exit;
311 end;
313 OutLen := Loader.GetAll(OutData);
314 alBufferData(
315 e_SoundsArray[find_id].alBuffer,
316 GetALSoundFormat(Loader.Format),
317 OutData,
318 OutLen,
319 Loader.Format.SampleRate
320 );
322 // don't need this anymore
323 Loader.Free();
324 Loader := nil;
326 if CheckALError() then
327 begin
328 e_LogWriteln('AL: what the fuck: ' + GetALError());
329 alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
330 e_SoundsArray[find_id].alBuffer := 0;
331 exit;
332 end;
333 end
334 else
335 begin
336 e_SoundsArray[find_id].alBuffer := 0;
337 e_SoundsArray[find_id].Loader := Loader;
338 end;
340 // the calling side won't free this, the loader will get a copy, so fuck it
341 FreeMem(pData);
342 ID := find_id;
343 Result := True;
344 end;
346 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
347 var
348 find_id: DWORD;
349 Loader: TSoundLoader;
350 OutData: Pointer;
351 OutLen: LongWord;
352 begin
353 ID := NO_SOUND_ID;
354 Result := False;
356 find_id := FindESound();
358 e_SoundsArray[find_id].Loader := nil;
359 e_SoundsArray[find_id].isMusic := isMusic;
360 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
361 e_SoundsArray[find_id].nRefs := 0;
363 Loader := e_GetSoundLoader(pData, LongWord(Length));
364 if Loader = nil then
365 begin
366 e_LogWritefln('Could not find loader for sound `%p`', [pData]);
367 exit;
368 end;
370 Loader.Looping := e_SoundsArray[find_id].Loops;
372 if not Loader.Load(pData, LongWord(Length), e_SoundsArray[find_id].isMusic) then
373 begin
374 e_LogWritefln('Could not load sound `%p`', [pData]);
375 exit;
376 end;
378 alGetError(); // reset error state, god damn it
380 if not Loader.Streaming then
381 begin
382 alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
383 if CheckALError() then
384 begin
385 e_LogWritefln('Could not create AL buffer for `%p`: %s', [pData, GetALError()]);
386 Loader.Free();
387 exit;
388 end;
390 OutLen := Loader.GetAll(OutData);
391 alBufferData(
392 e_SoundsArray[find_id].alBuffer,
393 GetALSoundFormat(Loader.Format),
394 OutData,
395 OutLen,
396 Loader.Format.SampleRate
397 );
399 // don't need this anymore
400 Loader.Free();
401 Loader := nil;
403 if CheckALError() then
404 begin
405 e_LogWriteln('AL: what the fuck: ' + GetALError());
406 alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
407 e_SoundsArray[find_id].alBuffer := 0;
408 exit;
409 end;
410 end
411 else
412 begin
413 e_SoundsArray[find_id].alBuffer := 0;
414 e_SoundsArray[find_id].Loader := Loader;
415 end;
417 // the calling side won't free this, the loader will get a copy, so fuck it
418 FreeMem(pData);
419 ID := find_id;
420 Result := True;
421 end;
423 function FindSourceForSound(ID: DWORD): Integer;
424 var
425 S: Integer;
426 begin
427 Result := -1;
428 if ID > High(e_SoundsArray) then
429 exit;
431 if e_SoundsArray[ID].Loader <> nil then
432 begin
433 // first source is for streaming sounds
434 // it always exists
435 alOwners[MUSIC_SOURCE] := nil;
436 Result := MUSIC_SOURCE;
437 exit;
438 end;
440 for S := 1 to High(alSources) do
441 if alSources[S] = 0 then
442 begin
443 alOwners[S] := nil; // TBasicSounds will set this if needed
444 Result := S;
445 break;
446 end;
448 if Result = -1 then Exit; // no voices left
450 alGetError(); // reset error state
451 alGenSources(1, @alSources[Result]);
452 if CheckALError() then
453 begin
454 e_LogWriteln('AL: FindSourceForSound(): alGenSources() failed: ' + GetALError());
455 Result := -1;
456 end;
457 end;
459 procedure AssignSound(ID: DWORD; Src: ALuint); inline;
460 var
461 S: ALint;
462 begin
463 alGetError(); // reset error state
465 if e_SoundsArray[ID].Loader <> nil then
466 begin
467 // this is a stream
468 // reset position
469 e_SoundsArray[ID].Loader.SetPosition(0);
470 if CurStream <> ID then // changing streams
471 begin
472 alSourceStop(Src); // this should mark all buffers as processed
473 alGetSourcei(Src, AL_BUFFERS_PROCESSED, S);
474 // unqueue all buffers
475 if S > 0 then
476 begin
477 alSourceUnqueueBuffers(Src, S, @alStreamBufs[alStreamAvail]);
478 alStreamAvail := NUM_STREAM_BUFFERS;
479 end;
480 end;
481 // this shit is playing now
482 CurStream := ID;
483 end
484 else
485 begin
486 // this is a full chunk, assign local buffer
487 alSourcei(Src, AL_BUFFER, e_SoundsArray[ID].alBuffer);
488 // these can loop
489 if (e_SoundsArray[ID].Loops) then
490 alSourcei(Src, AL_LOOPING, AL_TRUE)
491 else
492 alSourcei(Src, AL_LOOPING, AL_FALSE);
493 end;
495 alSourcei(Src, AL_SOURCE_RELATIVE, AL_TRUE);
496 end;
498 function e_PlaySound(ID: DWORD): Integer;
499 begin
500 Result := FindSourceForSound(ID);
501 if Result >= 0 then
502 begin
503 AssignSound(ID, alSources[Result]);
504 alSourcef(alSources[Result], AL_GAIN, 1);
505 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
506 alSourcePlay(alSources[Result]);
507 end;
508 end;
510 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
511 var
512 Pos: array [0..2] of ALfloat;
513 begin
514 Result := FindSourceForSound(ID);
515 if Result >= 0 then
516 begin
517 Pos[0] := Pan;
518 Pos[1] := 0;
519 Pos[2] := 0;
520 AssignSound(ID, alSources[Result]);
521 alSourcef(alSources[Result], AL_GAIN, 1);
522 alSourcefv(alSources[Result], AL_POSITION, Pos);
523 alSourcePlay(alSources[Result]);
524 end;
525 end;
527 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
528 begin
529 Result := FindSourceForSound(ID);
530 if Result >= 0 then
531 begin
532 AssignSound(ID, alSources[Result]);
533 alSourcef(alSources[Result], AL_GAIN, Volume);
534 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
535 alSourcePlay(alSources[Result]);
536 end;
537 end;
539 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
540 var
541 Pos: array [0..2] of ALfloat;
542 begin
543 Result := FindSourceForSound(ID);
544 if Result >= 0 then
545 begin
546 Pos[0] := Pan;
547 Pos[1] := 0;
548 Pos[2] := 0;
549 AssignSound(ID, alSources[Result]);
550 alSourcefv(alSources[Result], AL_POSITION, Pos);
551 alSourcef(alSources[Result], AL_GAIN, Volume);
552 alSourcePlay(alSources[Result]);
553 end;
554 end;
556 procedure e_DeleteSound(ID: DWORD);
557 begin
558 if ID > High(e_SoundsArray) then
559 exit;
560 if (e_SoundsArray[ID].alBuffer <> 0) then
561 begin
562 alDeleteBuffers(1, Addr(e_SoundsArray[ID].alBuffer));
563 e_SoundsArray[ID].alBuffer := 0;
564 end;
565 if (e_SoundsArray[ID].Loader <> nil) then
566 begin
567 e_SoundsArray[ID].Loader.Free();
568 e_SoundsArray[ID].Loader := nil;
569 if ID = CurStream then
570 CurStream := NO_SOUND_ID;
571 end;
572 end;
574 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
575 var
576 S: Integer;
577 V: ALfloat;
578 begin
579 // TODO: replace manual volume calculations everywhere with
580 // alListenerf(AL_GAIN) or something
581 if setMode then
582 begin
583 for S := 1 to High(alSources) do
584 if alSources[S] <> 0 then
585 alSourcef(alSources[S], AL_GAIN, SoundMod)
586 end
587 else
588 begin
589 for S := 1 to High(alSources) do
590 if alSources[S] <> 0 then
591 begin
592 alGetSourcef(alSources[S], AL_GAIN, V);
593 alSourcef(alSources[S], AL_GAIN, V * SoundMod);
594 end;
595 end;
596 end;
598 procedure e_MuteChannels(Enable: Boolean);
599 begin
600 if Enable = SoundMuted then
601 Exit;
603 SoundMuted := Enable;
604 end;
606 procedure e_StopChannels();
607 var
608 S: Integer;
609 begin
610 alGetError(); // reset error state
611 for S := Low(alSources) to High(alSources) do
612 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_PLAYING) then
613 begin
614 alSourceStop(alSources[S]);
615 alDeleteSources(1, @alSources[S]);
616 alSources[S] := 0;
617 end;
618 end;
620 procedure e_RemoveAllSounds();
621 var
622 i: Integer;
623 begin
624 for i := 0 to High(e_SoundsArray) do
625 if e_SoundsArray[i].alBuffer <> 0 then
626 e_DeleteSound(i);
627 SetLength(e_SoundsArray, 0);
628 e_SoundsArray := nil;
629 CurStream := NO_SOUND_ID;
630 end;
632 procedure e_ReleaseSoundSystem();
633 begin
634 e_RemoveAllSounds();
636 alcMakeContextCurrent(nil);
637 alcDestroyContext(alContext);
638 alcCloseDevice(alDevice);
640 alDevice := nil;
641 alContext := nil;
642 end;
644 procedure UpdateStreamSource(Src: Integer);
645 var
646 OutLen: LongWord;
647 Buf: ALuint;
648 S: Integer;
649 begin
650 if alSources[Src] = 0 then Exit;
652 alGetError(); // reset error state
654 alGetSourcei(alSources[Src], AL_BUFFERS_PROCESSED, S);
655 // unqueue processed buffers
656 if S > 0 then
657 begin
658 alSourceUnqueueBuffers(alSources[Src], S, @alStreamBufs[alStreamAvail]);
659 alStreamAvail := alStreamAvail + S;
660 end;
662 alGetError(); // reset error state
664 if (alStreamAvail > 0) and (CurStream <> NO_SOUND_ID) then
665 begin
666 // some buffers have freed up, advance stream playback
667 OutLen := e_SoundsArray[CurStream].Loader.FillBuffer(@alStreamData[0], STREAM_BUFSIZE);
668 if OutLen = 0 then Exit; // ran out of stream
669 Buf := alStreamBufs[alStreamAvail-1];
670 Dec(alStreamAvail);
671 // upload
672 alBufferData(
673 Buf,
674 GetALSoundFormat(e_SoundsArray[CurStream].Loader.Format),
675 @alStreamData[0],
676 OutLen,
677 e_SoundsArray[CurStream].Loader.Format.SampleRate
678 );
679 // attach
680 alSourceQueueBuffers(alSources[Src], 1, @Buf);
681 // restart if needed
682 S := GetALSourceState(alSources[Src]);
683 if (S = AL_STOPPED) or (S = AL_INITIAL) then
684 alSourcePlay(alSources[Src]);
685 end;
686 end;
688 procedure e_SoundUpdate();
689 var
690 S: Integer;
691 begin
692 alGetError(); // reset error state
694 // clear out all stopped sources
695 for S := 1 to High(alSources) do
696 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_STOPPED) then
697 begin
698 alDeleteSources(1, @alSources[S]);
699 alSources[S] := 0;
700 alOwners[S] := nil;
701 end;
703 // update the stream sources
704 UpdateStreamSource(MUSIC_SOURCE);
705 end;
707 { TBasicSound: }
709 constructor TBasicSound.Create();
710 begin
711 FID := NO_SOUND_ID;
712 FMusic := False;
713 FSource := -1;
714 FPosition := 0;
715 FMuted := False;
716 FOldGain := 1;
717 end;
719 destructor TBasicSound.Destroy();
720 begin
721 FreeSound();
722 inherited;
723 end;
725 function TBasicSound.InvalidSource(): Boolean; inline;
726 begin
727 Result := (FSource < 0) or (alSources[FSource] = 0) or (alOwners[FSource] <> self);
728 end;
730 procedure TBasicSound.FreeSound();
731 begin
732 if FID = NO_SOUND_ID then
733 Exit;
735 Stop();
736 FID := NO_SOUND_ID;
737 FMusic := False;
738 FPosition := 0;
739 end;
741 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
742 begin
743 Result := False;
744 if FID = NO_SOUND_ID then Exit;
746 if e_SoundsArray[FID].nRefs >= gMaxSimSounds then
747 begin
748 Result := True;
749 Exit;
750 end;
752 FSource := e_PlaySoundPanVolume(FID, Pan, Volume);
753 if FSource >= 0 then
754 begin
755 alOwners[FSource] := self;
756 Result := True;
757 end;
758 end;
760 procedure TBasicSound.SetID(ID: DWORD);
761 begin
762 FreeSound();
764 if ID > High(e_SoundsArray) then
765 exit;
767 FID := ID;
768 FMusic := e_SoundsArray[ID].isMusic;
769 end;
771 function TBasicSound.IsPlaying(): Boolean;
772 begin
773 Result := False;
774 if InvalidSource() then
775 Exit;
776 Result := GetALSourceState(alSources[FSource]) = AL_PLAYING;
777 end;
779 procedure TBasicSound.Stop();
780 begin
781 if FID = CurStream then
782 CurStream := NO_SOUND_ID;
783 if InvalidSource() then
784 Exit;
785 GetPosition();
786 alSourceStop(alSources[FSource]);
787 end;
789 function TBasicSound.IsPaused(): Boolean;
790 begin
791 Result := False;
792 if InvalidSource() then
793 Exit;
794 Result := GetALSourceState(alSources[FSource]) = AL_PAUSED;
795 end;
797 procedure TBasicSound.Pause(Enable: Boolean);
798 begin
799 if InvalidSource() then
800 Exit;
801 if Enable then
802 alSourcePause(alSources[FSource])
803 else
804 alSourcePlay(alSources[FSource]);
805 end;
807 function TBasicSound.GetVolume(): Single;
808 begin
809 Result := 0.0;
810 if InvalidSource() then
811 Exit;
812 alGetSourcef(alSources[FSource], AL_GAIN, Result);
813 end;
815 procedure TBasicSound.SetVolume(Volume: Single);
816 begin
817 if InvalidSource() then
818 Exit;
819 alSourcef(alSources[FSource], AL_GAIN, Volume);
820 end;
822 function TBasicSound.GetPan(): Single;
823 var
824 Pos: array [0..2] of ALfloat = (0, 0, 0);
825 begin
826 Result := 0.0;
827 if InvalidSource() then
828 Exit;
829 alGetSourcefv(alSources[FSource], AL_POSITION, Pos);
830 Result := Pos[0];
831 end;
833 procedure TBasicSound.SetPan(Pan: Single);
834 var
835 Pos: array [0..2] of ALfloat;
836 begin
837 if InvalidSource() then
838 Exit;
839 Pos[0] := Pan;
840 Pos[1] := 0;
841 Pos[2] := 0;
842 alSourcefv(alSources[FSource], AL_POSITION, Pos);
843 end;
845 function TBasicSound.IsMuted(): Boolean;
846 begin
847 if InvalidSource() then
848 Result := False
849 else
850 Result := FMuted;
851 end;
853 procedure TBasicSound.Mute(Enable: Boolean);
854 begin
855 if InvalidSource() then
856 Exit;
857 if Enable then
858 begin
859 FOldGain := GetVolume();
860 FMuted := True;
861 SetVolume(0);
862 end
863 else if FMuted then
864 begin
865 FMuted := False;
866 SetVolume(FOldGain);
867 end;
868 end;
870 function TBasicSound.GetPosition(): DWORD;
871 var
872 Bytes: ALint;
873 begin
874 Result := 0;
875 if InvalidSource() then
876 Exit;
877 alGetSourcei(alSources[FSource], AL_BYTE_OFFSET, Bytes);
878 FPosition := Bytes;
879 Result := FPosition;
880 end;
882 procedure TBasicSound.SetPosition(aPos: DWORD);
883 begin
884 FPosition := aPos;
885 if InvalidSource() then
886 Exit;
887 alSourcei(alSources[FSource], AL_BYTE_OFFSET, aPos);
888 end;
890 procedure TBasicSound.SetPriority(priority: Integer);
891 begin
892 end;
894 end.