DEADSOFTWARE

Sound: OpenAL: Expose some vars;
[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 ID := find_id;
341 Result := True;
342 end;
344 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
345 var
346 find_id: DWORD;
347 Loader: TSoundLoader;
348 OutData: Pointer;
349 OutLen: LongWord;
350 begin
351 ID := NO_SOUND_ID;
352 Result := False;
354 find_id := FindESound();
356 e_SoundsArray[find_id].Loader := nil;
357 e_SoundsArray[find_id].isMusic := isMusic;
358 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
359 e_SoundsArray[find_id].nRefs := 0;
361 Loader := e_GetSoundLoader(pData, LongWord(Length));
362 if Loader = nil then
363 begin
364 e_LogWritefln('Could not find loader for sound `%p`', [pData]);
365 exit;
366 end;
368 Loader.Looping := e_SoundsArray[find_id].Loops;
370 if not Loader.Load(pData, LongWord(Length), e_SoundsArray[find_id].isMusic) then
371 begin
372 e_LogWritefln('Could not load sound `%p`', [pData]);
373 exit;
374 end;
376 alGetError(); // reset error state, god damn it
378 if not Loader.Streaming then
379 begin
380 alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
381 if CheckALError() then
382 begin
383 e_LogWritefln('Could not create AL buffer for `%p`: %s', [pData, GetALError()]);
384 Loader.Free();
385 exit;
386 end;
388 OutLen := Loader.GetAll(OutData);
389 alBufferData(
390 e_SoundsArray[find_id].alBuffer,
391 GetALSoundFormat(Loader.Format),
392 OutData,
393 OutLen,
394 Loader.Format.SampleRate
395 );
397 // don't need this anymore
398 Loader.Free();
399 Loader := nil;
401 if CheckALError() then
402 begin
403 e_LogWriteln('AL: what the fuck: ' + GetALError());
404 alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
405 e_SoundsArray[find_id].alBuffer := 0;
406 exit;
407 end;
408 end
409 else
410 begin
411 e_SoundsArray[find_id].alBuffer := 0;
412 e_SoundsArray[find_id].Loader := Loader;
413 end;
415 ID := find_id;
416 Result := True;
417 end;
419 function FindSourceForSound(ID: DWORD): Integer;
420 var
421 S: Integer;
422 begin
423 Result := -1;
424 if ID > High(e_SoundsArray) then
425 exit;
427 if e_SoundsArray[ID].Loader <> nil then
428 begin
429 // first source is for streaming sounds
430 // it always exists
431 alOwners[MUSIC_SOURCE] := nil;
432 Result := MUSIC_SOURCE;
433 exit;
434 end;
436 for S := 1 to High(alSources) do
437 if alSources[S] = 0 then
438 begin
439 alOwners[S] := nil; // TBasicSounds will set this if needed
440 Result := S;
441 break;
442 end;
444 if Result = -1 then Exit; // no voices left
446 alGetError(); // reset error state
447 alGenSources(1, @alSources[Result]);
448 if CheckALError() then
449 begin
450 e_LogWriteln('AL: FindSourceForSound(): alGenSources() failed: ' + GetALError());
451 Result := -1;
452 end;
453 end;
455 procedure AssignSound(ID: DWORD; Src: ALuint); inline;
456 var
457 S: ALint;
458 begin
459 alGetError(); // reset error state
461 if e_SoundsArray[ID].Loader <> nil then
462 begin
463 // this is a stream
464 // reset position
465 e_SoundsArray[ID].Loader.SetPosition(0);
466 if CurStream <> ID then // changing streams
467 begin
468 alSourceStop(Src); // this should mark all buffers as processed
469 alGetSourcei(Src, AL_BUFFERS_PROCESSED, S);
470 // unqueue all buffers
471 if S > 0 then
472 begin
473 alSourceUnqueueBuffers(Src, S, @alStreamBufs[alStreamAvail]);
474 alStreamAvail := NUM_STREAM_BUFFERS;
475 end;
476 end;
477 // this shit is playing now
478 CurStream := ID;
479 end
480 else
481 begin
482 // this is a full chunk, assign local buffer
483 alSourcei(Src, AL_BUFFER, e_SoundsArray[ID].alBuffer);
484 // these can loop
485 if (e_SoundsArray[ID].Loops) then
486 alSourcei(Src, AL_LOOPING, AL_TRUE)
487 else
488 alSourcei(Src, AL_LOOPING, AL_FALSE);
489 end;
491 alSourcei(Src, AL_SOURCE_RELATIVE, AL_TRUE);
492 end;
494 function e_PlaySound(ID: DWORD): Integer;
495 begin
496 Result := FindSourceForSound(ID);
497 if Result >= 0 then
498 begin
499 AssignSound(ID, alSources[Result]);
500 alSourcef(alSources[Result], AL_GAIN, 1);
501 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
502 alSourcePlay(alSources[Result]);
503 end;
504 end;
506 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
507 var
508 Pos: array [0..2] of ALfloat;
509 begin
510 Result := FindSourceForSound(ID);
511 if Result >= 0 then
512 begin
513 Pos[0] := Pan;
514 Pos[1] := 0;
515 Pos[2] := 0;
516 AssignSound(ID, alSources[Result]);
517 alSourcef(alSources[Result], AL_GAIN, 1);
518 alSourcefv(alSources[Result], AL_POSITION, Pos);
519 alSourcePlay(alSources[Result]);
520 end;
521 end;
523 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
524 begin
525 Result := FindSourceForSound(ID);
526 if Result >= 0 then
527 begin
528 AssignSound(ID, alSources[Result]);
529 alSourcef(alSources[Result], AL_GAIN, Volume);
530 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
531 alSourcePlay(alSources[Result]);
532 end;
533 end;
535 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
536 var
537 Pos: array [0..2] of ALfloat;
538 begin
539 Result := FindSourceForSound(ID);
540 if Result >= 0 then
541 begin
542 Pos[0] := Pan;
543 Pos[1] := 0;
544 Pos[2] := 0;
545 AssignSound(ID, alSources[Result]);
546 alSourcefv(alSources[Result], AL_POSITION, Pos);
547 alSourcef(alSources[Result], AL_GAIN, Volume);
548 alSourcePlay(alSources[Result]);
549 end;
550 end;
552 procedure e_DeleteSound(ID: DWORD);
553 begin
554 if ID > High(e_SoundsArray) then
555 exit;
556 if (e_SoundsArray[ID].alBuffer <> 0) then
557 begin
558 alDeleteBuffers(1, Addr(e_SoundsArray[ID].alBuffer));
559 e_SoundsArray[ID].alBuffer := 0;
560 end;
561 if (e_SoundsArray[ID].Loader <> nil) then
562 begin
563 e_SoundsArray[ID].Loader.Free();
564 e_SoundsArray[ID].Loader := nil;
565 if ID = CurStream then
566 CurStream := NO_SOUND_ID;
567 end;
568 end;
570 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
571 var
572 S: Integer;
573 V: ALfloat;
574 begin
575 // TODO: replace manual volume calculations everywhere with
576 // alListenerf(AL_GAIN) or something
577 if setMode then
578 begin
579 for S := 1 to High(alSources) do
580 if alSources[S] <> 0 then
581 alSourcef(alSources[S], AL_GAIN, SoundMod)
582 end
583 else
584 begin
585 for S := 1 to High(alSources) do
586 if alSources[S] <> 0 then
587 begin
588 alGetSourcef(alSources[S], AL_GAIN, V);
589 alSourcef(alSources[S], AL_GAIN, V * SoundMod);
590 end;
591 end;
592 end;
594 procedure e_MuteChannels(Enable: Boolean);
595 begin
596 if Enable = SoundMuted then
597 Exit;
599 SoundMuted := Enable;
600 end;
602 procedure e_StopChannels();
603 var
604 S: Integer;
605 begin
606 alGetError(); // reset error state
607 for S := Low(alSources) to High(alSources) do
608 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_PLAYING) then
609 begin
610 alSourceStop(alSources[S]);
611 alDeleteSources(1, @alSources[S]);
612 alSources[S] := 0;
613 end;
614 end;
616 procedure e_RemoveAllSounds();
617 var
618 i: Integer;
619 begin
620 for i := 0 to High(e_SoundsArray) do
621 if e_SoundsArray[i].alBuffer <> 0 then
622 e_DeleteSound(i);
623 SetLength(e_SoundsArray, 0);
624 e_SoundsArray := nil;
625 CurStream := NO_SOUND_ID;
626 end;
628 procedure e_ReleaseSoundSystem();
629 begin
630 e_RemoveAllSounds();
632 alcMakeContextCurrent(nil);
633 alcDestroyContext(alContext);
634 alcCloseDevice(alDevice);
636 alDevice := nil;
637 alContext := nil;
638 end;
640 procedure UpdateStreamSource(Src: Integer);
641 var
642 OutLen: LongWord;
643 Buf: ALuint;
644 S: Integer;
645 begin
646 if alSources[Src] = 0 then Exit;
648 alGetError(); // reset error state
650 alGetSourcei(alSources[Src], AL_BUFFERS_PROCESSED, S);
651 // unqueue processed buffers
652 if S > 0 then
653 begin
654 alSourceUnqueueBuffers(alSources[Src], S, @alStreamBufs[alStreamAvail]);
655 alStreamAvail := alStreamAvail + S;
656 end;
658 alGetError(); // reset error state
660 if (alStreamAvail > 0) and (CurStream <> NO_SOUND_ID) then
661 begin
662 // some buffers have freed up, advance stream playback
663 OutLen := e_SoundsArray[CurStream].Loader.FillBuffer(@alStreamData[0], STREAM_BUFSIZE);
664 if OutLen = 0 then Exit; // ran out of stream
665 Buf := alStreamBufs[alStreamAvail-1];
666 Dec(alStreamAvail);
667 // upload
668 alBufferData(
669 Buf,
670 GetALSoundFormat(e_SoundsArray[CurStream].Loader.Format),
671 @alStreamData[0],
672 OutLen,
673 e_SoundsArray[CurStream].Loader.Format.SampleRate
674 );
675 // attach
676 alSourceQueueBuffers(alSources[Src], 1, @Buf);
677 // restart if needed
678 S := GetALSourceState(alSources[Src]);
679 if (S = AL_STOPPED) or (S = AL_INITIAL) then
680 alSourcePlay(alSources[Src]);
681 end;
682 end;
684 procedure e_SoundUpdate();
685 var
686 S: Integer;
687 begin
688 alGetError(); // reset error state
690 // clear out all stopped sources
691 for S := 1 to High(alSources) do
692 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_STOPPED) then
693 begin
694 alDeleteSources(1, @alSources[S]);
695 alSources[S] := 0;
696 alOwners[S] := nil;
697 end;
699 // update the stream sources
700 UpdateStreamSource(MUSIC_SOURCE);
701 end;
703 { TBasicSound: }
705 constructor TBasicSound.Create();
706 begin
707 FID := NO_SOUND_ID;
708 FMusic := False;
709 FSource := -1;
710 FPosition := 0;
711 FMuted := False;
712 FOldGain := 1;
713 end;
715 destructor TBasicSound.Destroy();
716 begin
717 FreeSound();
718 inherited;
719 end;
721 function TBasicSound.InvalidSource(): Boolean; inline;
722 begin
723 Result := (FSource < 0) or (alSources[FSource] = 0) or (alOwners[FSource] <> self);
724 end;
726 procedure TBasicSound.FreeSound();
727 begin
728 if FID = NO_SOUND_ID then
729 Exit;
731 Stop();
732 FID := NO_SOUND_ID;
733 FMusic := False;
734 FPosition := 0;
735 end;
737 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
738 begin
739 Result := False;
740 if FID = NO_SOUND_ID then Exit;
742 if e_SoundsArray[FID].nRefs >= gMaxSimSounds then
743 begin
744 Result := True;
745 Exit;
746 end;
748 FSource := e_PlaySoundPanVolume(FID, Pan, Volume);
749 if FSource >= 0 then
750 begin
751 alOwners[FSource] := self;
752 Result := True;
753 end;
754 end;
756 procedure TBasicSound.SetID(ID: DWORD);
757 begin
758 FreeSound();
760 if ID > High(e_SoundsArray) then
761 exit;
763 FID := ID;
764 FMusic := e_SoundsArray[ID].isMusic;
765 end;
767 function TBasicSound.IsPlaying(): Boolean;
768 begin
769 Result := False;
770 if InvalidSource() then
771 Exit;
772 Result := GetALSourceState(alSources[FSource]) = AL_PLAYING;
773 end;
775 procedure TBasicSound.Stop();
776 begin
777 if FID = CurStream then
778 CurStream := NO_SOUND_ID;
779 if InvalidSource() then
780 Exit;
781 GetPosition();
782 alSourceStop(alSources[FSource]);
783 end;
785 function TBasicSound.IsPaused(): Boolean;
786 begin
787 Result := False;
788 if InvalidSource() then
789 Exit;
790 Result := GetALSourceState(alSources[FSource]) = AL_PAUSED;
791 end;
793 procedure TBasicSound.Pause(Enable: Boolean);
794 begin
795 if InvalidSource() then
796 Exit;
797 if Enable then
798 alSourcePause(alSources[FSource])
799 else
800 alSourcePlay(alSources[FSource]);
801 end;
803 function TBasicSound.GetVolume(): Single;
804 begin
805 Result := 0.0;
806 if InvalidSource() then
807 Exit;
808 alGetSourcef(alSources[FSource], AL_GAIN, Result);
809 end;
811 procedure TBasicSound.SetVolume(Volume: Single);
812 begin
813 if InvalidSource() then
814 Exit;
815 alSourcef(alSources[FSource], AL_GAIN, Volume);
816 end;
818 function TBasicSound.GetPan(): Single;
819 var
820 Pos: array [0..2] of ALfloat = (0, 0, 0);
821 begin
822 Result := 0.0;
823 if InvalidSource() then
824 Exit;
825 alGetSourcefv(alSources[FSource], AL_POSITION, Pos);
826 Result := Pos[0];
827 end;
829 procedure TBasicSound.SetPan(Pan: Single);
830 var
831 Pos: array [0..2] of ALfloat;
832 begin
833 if InvalidSource() then
834 Exit;
835 Pos[0] := Pan;
836 Pos[1] := 0;
837 Pos[2] := 0;
838 alSourcefv(alSources[FSource], AL_POSITION, Pos);
839 end;
841 function TBasicSound.IsMuted(): Boolean;
842 begin
843 if InvalidSource() then
844 Result := False
845 else
846 Result := FMuted;
847 end;
849 procedure TBasicSound.Mute(Enable: Boolean);
850 begin
851 if InvalidSource() then
852 Exit;
853 if Enable then
854 begin
855 FOldGain := GetVolume();
856 FMuted := True;
857 SetVolume(0);
858 end
859 else if FMuted then
860 begin
861 FMuted := False;
862 SetVolume(FOldGain);
863 end;
864 end;
866 function TBasicSound.GetPosition(): DWORD;
867 var
868 Bytes: ALint;
869 begin
870 Result := 0;
871 if InvalidSource() then
872 Exit;
873 alGetSourcei(alSources[FSource], AL_BYTE_OFFSET, Bytes);
874 FPosition := Bytes;
875 Result := FPosition;
876 end;
878 procedure TBasicSound.SetPosition(aPos: DWORD);
879 begin
880 FPosition := aPos;
881 if InvalidSource() then
882 Exit;
883 alSourcei(alSources[FSource], AL_BYTE_OFFSET, aPos);
884 end;
886 procedure TBasicSound.SetPriority(priority: Integer);
887 begin
888 end;
890 end.