DEADSOFTWARE

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