DEADSOFTWARE

04305b61ec0b4ce522dd4dfd99f9f594267d44a4
[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 // the calling side won't free this, the loader will get a copy, so fuck it
416 FreeMem(pData);
417 ID := find_id;
418 Result := True;
419 end;
421 function FindSourceForSound(ID: DWORD): Integer;
422 var
423 S: Integer;
424 begin
425 Result := -1;
426 if ID > High(e_SoundsArray) then
427 exit;
429 if e_SoundsArray[ID].Loader <> nil then
430 begin
431 // first source is for streaming sounds
432 // it always exists
433 alOwners[MUSIC_SOURCE] := nil;
434 Result := MUSIC_SOURCE;
435 exit;
436 end;
438 for S := 1 to High(alSources) do
439 if alSources[S] = 0 then
440 begin
441 alOwners[S] := nil; // TBasicSounds will set this if needed
442 Result := S;
443 break;
444 end;
446 if Result = -1 then Exit; // no voices left
448 alGetError(); // reset error state
449 alGenSources(1, @alSources[Result]);
450 if CheckALError() then
451 begin
452 e_LogWriteln('AL: FindSourceForSound(): alGenSources() failed: ' + GetALError());
453 Result := -1;
454 end;
455 end;
457 procedure AssignSound(ID: DWORD; Src: ALuint); inline;
458 var
459 S: ALint;
460 begin
461 alGetError(); // reset error state
463 if e_SoundsArray[ID].Loader <> nil then
464 begin
465 // this is a stream
466 // reset position
467 e_SoundsArray[ID].Loader.SetPosition(0);
468 if CurStream <> ID then // changing streams
469 begin
470 alSourceStop(Src); // this should mark all buffers as processed
471 alGetSourcei(Src, AL_BUFFERS_PROCESSED, S);
472 // unqueue all buffers
473 if S > 0 then
474 begin
475 alSourceUnqueueBuffers(Src, S, @alStreamBufs[alStreamAvail]);
476 alStreamAvail := NUM_STREAM_BUFFERS;
477 end;
478 end;
479 // this shit is playing now
480 CurStream := ID;
481 end
482 else
483 begin
484 // this is a full chunk, assign local buffer
485 alSourcei(Src, AL_BUFFER, e_SoundsArray[ID].alBuffer);
486 // these can loop
487 if (e_SoundsArray[ID].Loops) then
488 alSourcei(Src, AL_LOOPING, AL_TRUE)
489 else
490 alSourcei(Src, AL_LOOPING, AL_FALSE);
491 end;
493 alSourcei(Src, AL_SOURCE_RELATIVE, AL_TRUE);
494 end;
496 function e_PlaySound(ID: DWORD): Integer;
497 begin
498 Result := FindSourceForSound(ID);
499 if Result >= 0 then
500 begin
501 AssignSound(ID, alSources[Result]);
502 alSourcef(alSources[Result], AL_GAIN, 1);
503 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
504 alSourcePlay(alSources[Result]);
505 end;
506 end;
508 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
509 var
510 Pos: array [0..2] of ALfloat;
511 begin
512 Result := FindSourceForSound(ID);
513 if Result >= 0 then
514 begin
515 Pos[0] := Pan;
516 Pos[1] := 0;
517 Pos[2] := 0;
518 AssignSound(ID, alSources[Result]);
519 alSourcef(alSources[Result], AL_GAIN, 1);
520 alSourcefv(alSources[Result], AL_POSITION, Pos);
521 alSourcePlay(alSources[Result]);
522 end;
523 end;
525 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
526 begin
527 Result := FindSourceForSound(ID);
528 if Result >= 0 then
529 begin
530 AssignSound(ID, alSources[Result]);
531 alSourcef(alSources[Result], AL_GAIN, Volume);
532 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
533 alSourcePlay(alSources[Result]);
534 end;
535 end;
537 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
538 var
539 Pos: array [0..2] of ALfloat;
540 begin
541 Result := FindSourceForSound(ID);
542 if Result >= 0 then
543 begin
544 Pos[0] := Pan;
545 Pos[1] := 0;
546 Pos[2] := 0;
547 AssignSound(ID, alSources[Result]);
548 alSourcefv(alSources[Result], AL_POSITION, Pos);
549 alSourcef(alSources[Result], AL_GAIN, Volume);
550 alSourcePlay(alSources[Result]);
551 end;
552 end;
554 procedure e_DeleteSound(ID: DWORD);
555 begin
556 if ID > High(e_SoundsArray) then
557 exit;
558 if (e_SoundsArray[ID].alBuffer <> 0) then
559 begin
560 alDeleteBuffers(1, Addr(e_SoundsArray[ID].alBuffer));
561 e_SoundsArray[ID].alBuffer := 0;
562 end;
563 if (e_SoundsArray[ID].Loader <> nil) then
564 begin
565 e_SoundsArray[ID].Loader.Free();
566 e_SoundsArray[ID].Loader := nil;
567 if ID = CurStream then
568 CurStream := NO_SOUND_ID;
569 end;
570 end;
572 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
573 var
574 S: Integer;
575 V: ALfloat;
576 begin
577 // TODO: replace manual volume calculations everywhere with
578 // alListenerf(AL_GAIN) or something
579 if setMode then
580 begin
581 for S := 1 to High(alSources) do
582 if alSources[S] <> 0 then
583 alSourcef(alSources[S], AL_GAIN, SoundMod)
584 end
585 else
586 begin
587 for S := 1 to High(alSources) do
588 if alSources[S] <> 0 then
589 begin
590 alGetSourcef(alSources[S], AL_GAIN, V);
591 alSourcef(alSources[S], AL_GAIN, V * SoundMod);
592 end;
593 end;
594 end;
596 procedure e_MuteChannels(Enable: Boolean);
597 begin
598 if Enable = SoundMuted then
599 Exit;
601 SoundMuted := Enable;
602 end;
604 procedure e_StopChannels();
605 var
606 S: Integer;
607 begin
608 alGetError(); // reset error state
609 for S := Low(alSources) to High(alSources) do
610 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_PLAYING) then
611 begin
612 alSourceStop(alSources[S]);
613 alDeleteSources(1, @alSources[S]);
614 alSources[S] := 0;
615 end;
616 end;
618 procedure e_RemoveAllSounds();
619 var
620 i: Integer;
621 begin
622 for i := 0 to High(e_SoundsArray) do
623 if e_SoundsArray[i].alBuffer <> 0 then
624 e_DeleteSound(i);
625 SetLength(e_SoundsArray, 0);
626 e_SoundsArray := nil;
627 CurStream := NO_SOUND_ID;
628 end;
630 procedure e_ReleaseSoundSystem();
631 begin
632 e_RemoveAllSounds();
634 alcMakeContextCurrent(nil);
635 alcDestroyContext(alContext);
636 alcCloseDevice(alDevice);
638 alDevice := nil;
639 alContext := nil;
640 end;
642 procedure UpdateStreamSource(Src: Integer);
643 var
644 OutLen: LongWord;
645 Buf: ALuint;
646 S: Integer;
647 begin
648 if alSources[Src] = 0 then Exit;
650 alGetError(); // reset error state
652 alGetSourcei(alSources[Src], AL_BUFFERS_PROCESSED, S);
653 // unqueue processed buffers
654 if S > 0 then
655 begin
656 alSourceUnqueueBuffers(alSources[Src], S, @alStreamBufs[alStreamAvail]);
657 alStreamAvail := alStreamAvail + S;
658 end;
660 alGetError(); // reset error state
662 if (alStreamAvail > 0) and (CurStream <> NO_SOUND_ID) then
663 begin
664 // some buffers have freed up, advance stream playback
665 OutLen := e_SoundsArray[CurStream].Loader.FillBuffer(@alStreamData[0], STREAM_BUFSIZE);
666 if OutLen = 0 then Exit; // ran out of stream
667 Buf := alStreamBufs[alStreamAvail-1];
668 Dec(alStreamAvail);
669 // upload
670 alBufferData(
671 Buf,
672 GetALSoundFormat(e_SoundsArray[CurStream].Loader.Format),
673 @alStreamData[0],
674 OutLen,
675 e_SoundsArray[CurStream].Loader.Format.SampleRate
676 );
677 // attach
678 alSourceQueueBuffers(alSources[Src], 1, @Buf);
679 // restart if needed
680 S := GetALSourceState(alSources[Src]);
681 if (S = AL_STOPPED) or (S = AL_INITIAL) then
682 alSourcePlay(alSources[Src]);
683 end;
684 end;
686 procedure e_SoundUpdate();
687 var
688 S: Integer;
689 begin
690 alGetError(); // reset error state
692 // clear out all stopped sources
693 for S := 1 to High(alSources) do
694 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_STOPPED) then
695 begin
696 alDeleteSources(1, @alSources[S]);
697 alSources[S] := 0;
698 alOwners[S] := nil;
699 end;
701 // update the stream sources
702 UpdateStreamSource(MUSIC_SOURCE);
703 end;
705 { TBasicSound: }
707 constructor TBasicSound.Create();
708 begin
709 FID := NO_SOUND_ID;
710 FMusic := False;
711 FSource := -1;
712 FPosition := 0;
713 FMuted := False;
714 FOldGain := 1;
715 end;
717 destructor TBasicSound.Destroy();
718 begin
719 FreeSound();
720 inherited;
721 end;
723 function TBasicSound.InvalidSource(): Boolean; inline;
724 begin
725 Result := (FSource < 0) or (alSources[FSource] = 0) or (alOwners[FSource] <> self);
726 end;
728 procedure TBasicSound.FreeSound();
729 begin
730 if FID = NO_SOUND_ID then
731 Exit;
733 Stop();
734 FID := NO_SOUND_ID;
735 FMusic := False;
736 FPosition := 0;
737 end;
739 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
740 begin
741 Result := False;
742 if FID = NO_SOUND_ID then Exit;
744 if e_SoundsArray[FID].nRefs >= gMaxSimSounds then
745 begin
746 Result := True;
747 Exit;
748 end;
750 FSource := e_PlaySoundPanVolume(FID, Pan, Volume);
751 if FSource >= 0 then
752 begin
753 alOwners[FSource] := self;
754 Result := True;
755 end;
756 end;
758 procedure TBasicSound.SetID(ID: DWORD);
759 begin
760 FreeSound();
762 if ID > High(e_SoundsArray) then
763 exit;
765 FID := ID;
766 FMusic := e_SoundsArray[ID].isMusic;
767 end;
769 function TBasicSound.IsPlaying(): Boolean;
770 begin
771 Result := False;
772 if InvalidSource() then
773 Exit;
774 Result := GetALSourceState(alSources[FSource]) = AL_PLAYING;
775 end;
777 procedure TBasicSound.Stop();
778 begin
779 if FID = CurStream then
780 CurStream := NO_SOUND_ID;
781 if InvalidSource() then
782 Exit;
783 GetPosition();
784 alSourceStop(alSources[FSource]);
785 end;
787 function TBasicSound.IsPaused(): Boolean;
788 begin
789 Result := False;
790 if InvalidSource() then
791 Exit;
792 Result := GetALSourceState(alSources[FSource]) = AL_PAUSED;
793 end;
795 procedure TBasicSound.Pause(Enable: Boolean);
796 begin
797 if InvalidSource() then
798 Exit;
799 if Enable then
800 alSourcePause(alSources[FSource])
801 else
802 alSourcePlay(alSources[FSource]);
803 end;
805 function TBasicSound.GetVolume(): Single;
806 begin
807 Result := 0.0;
808 if InvalidSource() then
809 Exit;
810 alGetSourcef(alSources[FSource], AL_GAIN, Result);
811 end;
813 procedure TBasicSound.SetVolume(Volume: Single);
814 begin
815 if InvalidSource() then
816 Exit;
817 alSourcef(alSources[FSource], AL_GAIN, Volume);
818 end;
820 function TBasicSound.GetPan(): Single;
821 var
822 Pos: array [0..2] of ALfloat = (0, 0, 0);
823 begin
824 Result := 0.0;
825 if InvalidSource() then
826 Exit;
827 alGetSourcefv(alSources[FSource], AL_POSITION, Pos);
828 Result := Pos[0];
829 end;
831 procedure TBasicSound.SetPan(Pan: Single);
832 var
833 Pos: array [0..2] of ALfloat;
834 begin
835 if InvalidSource() then
836 Exit;
837 Pos[0] := Pan;
838 Pos[1] := 0;
839 Pos[2] := 0;
840 alSourcefv(alSources[FSource], AL_POSITION, Pos);
841 end;
843 function TBasicSound.IsMuted(): Boolean;
844 begin
845 if InvalidSource() then
846 Result := False
847 else
848 Result := FMuted;
849 end;
851 procedure TBasicSound.Mute(Enable: Boolean);
852 begin
853 if InvalidSource() then
854 Exit;
855 if Enable then
856 begin
857 FOldGain := GetVolume();
858 FMuted := True;
859 SetVolume(0);
860 end
861 else if FMuted then
862 begin
863 FMuted := False;
864 SetVolume(FOldGain);
865 end;
866 end;
868 function TBasicSound.GetPosition(): DWORD;
869 var
870 Bytes: ALint;
871 begin
872 Result := 0;
873 if InvalidSource() then
874 Exit;
875 alGetSourcei(alSources[FSource], AL_BYTE_OFFSET, Bytes);
876 FPosition := Bytes;
877 Result := FPosition;
878 end;
880 procedure TBasicSound.SetPosition(aPos: DWORD);
881 begin
882 FPosition := aPos;
883 if InvalidSource() then
884 Exit;
885 alSourcei(alSources[FSource], AL_BYTE_OFFSET, aPos);
886 end;
888 procedure TBasicSound.SetPriority(priority: Integer);
889 begin
890 end;
892 end.