DEADSOFTWARE

Sound: OpenAL: Add MIDI support via FluidSynth
[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;
97 implementation
99 uses
100 g_window, g_options, utils;
102 const
103 NUM_SOURCES = 255; // + 1 stereo
104 NUM_STREAM_BUFFERS = 8;
105 STREAM_BUFSIZE = 8192;
106 MUSIC_SOURCE = 0;
108 var
109 SoundMuted: Boolean = False;
110 CurStream: DWORD = NO_SOUND_ID;
111 alDevice: PALCdevice = nil;
112 alContext: PALCcontext = nil;
113 // sources for everything
114 alSources: array [0..NUM_SOURCES] of ALuint;
115 // last TBasicSound that has played on each source
116 alOwners: array [0..NUM_SOURCES] of TBasicSound;
117 // buffers for the music stream
118 alStreamBufs: array [0..NUM_STREAM_BUFFERS-1] of ALuint;
119 alStreamData: array [0..STREAM_BUFSIZE-1] of Byte;
120 alStreamAvail: Integer = NUM_STREAM_BUFFERS;
122 function CheckALError(): Boolean;
123 begin
124 e_ALError := alGetError();
125 Result := e_ALError <> AL_NO_ERROR;
126 end;
128 function GetALError(): string;
129 begin
130 Result := '';
131 case e_ALError of
132 AL_NO_ERROR: Result := '';
133 AL_INVALID_NAME: Result := 'AL_INVALID_NAME';
134 AL_INVALID_ENUM: Result := 'AL_INVALID_ENUM';
135 AL_INVALID_VALUE: Result := 'AL_INVALID_VALUE';
136 AL_INVALID_OPERATION: Result := 'AL_INVALID_OPERATION';
137 AL_OUT_OF_MEMORY: Result := 'AL_OUT_OF_MEMORY';
138 else Result := Format('unknown error %x', [e_ALError]);
139 end;
140 end;
142 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
143 var
144 alExt, alRend, alVendor, alVer: string;
145 WantDev: string = '';
146 WantAttrs: array [0..4] of ALCint = (
147 ALC_STEREO_SOURCES, 1,
148 ALC_MONO_SOURCES, NUM_SOURCES,
150 );
151 begin
152 Result := False;
154 WantDev := alcGetString(nil, ALC_DEVICE_SPECIFIER);
155 e_LogWritefln('AL: available devices: %s', [WantDev]);
157 // TODO: open a dummy device when NoOutput is true or something
158 WantDev := alcGetString(nil, ALC_DEFAULT_DEVICE_SPECIFIER);
159 e_LogWritefln('AL: trying to open device %s', [WantDev]);
161 alDevice := alcOpenDevice(PChar(WantDev));
162 if alDevice = nil then
163 begin
164 e_LogWritefln('AL: ERROR: could not open device %s: %s', [WantDev, GetALError()]);
165 exit;
166 end;
168 alContext := alcCreateContext(alDevice, WantAttrs);
169 if alContext = nil then
170 begin
171 e_LogWritefln('AL: ERROR: could not create context: %s', [GetALError()]);
172 alcCloseDevice(alDevice);
173 alDevice := nil;
174 exit;
175 end;
177 alcMakeContextCurrent(alContext);
179 // TODO: actually parse these from alc attributes or something
180 e_SoundFormat.SampleRate := 48000;
181 e_SoundFormat.SampleBits := 16;
182 e_SoundFormat.Channels := 2;
184 alVendor := alGetString(AL_VENDOR);
185 alRend := alGetString(AL_RENDERER);
186 alVer := alGetString(AL_VERSION);
187 alExt := alGetString(AL_EXTENSIONS);
189 e_LogWriteln('AL INFO:');
190 e_LogWriteln(' Version: ' + alVer);
191 e_LogWriteln(' Vendor: ' + alVendor);
192 e_LogWriteln(' Renderer: ' + alRend);
193 e_LogWriteln(' Device: ' + WantDev);
194 e_LogWriteln(' Sample rate: ' + IntToStr(e_SoundFormat.SampleRate));
195 e_LogWriteln(' Extensions:');
196 e_LogWriteln(' ' + alExt);
198 ZeroMemory(@alSources[0], sizeof(alSources));
199 ZeroMemory(@alOwners[0], sizeof(alOwners));
200 ZeroMemory(@alStreamBufs[0], sizeof(alStreamBufs));
201 ZeroMemory(@alStreamData[0], sizeof(alStreamData));
202 CurStream := NO_SOUND_ID;
204 alGetError(); // reset the goddamn error state
205 alGenSources(1, @alSources[0]); // generate the music source
206 if CheckALError() then
207 e_LogWriteln('AL: ERROR: alGenSources() for music failed: ' + GetALError());
209 alStreamAvail := 0;
210 alGenBuffers(NUM_STREAM_BUFFERS, @alStreamBufs[0]); // generate buffers for the music stream
211 if CheckALError() then
212 e_LogWriteln('AL: ERROR: alGenSources() for music failed: ' + GetALError())
213 else
214 alStreamAvail := NUM_STREAM_BUFFERS;
216 Result := True;
217 end;
219 function FindESound(): DWORD;
220 var
221 i: Integer;
223 begin
224 if e_SoundsArray <> nil then
225 for i := 0 to High(e_SoundsArray) do
226 if (e_SoundsArray[i].alBuffer = 0) and (e_SoundsArray[i].Loader = nil) then
227 begin
228 Result := i;
229 Exit;
230 end;
232 if e_SoundsArray = nil then
233 begin
234 SetLength(e_SoundsArray, 16);
235 Result := 0;
236 end
237 else
238 begin
239 Result := High(e_SoundsArray) + 1;
240 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
241 end;
242 end;
244 function GetALSoundFormat(Fmt: TSoundFormat): ALenum; inline;
245 begin
246 if Fmt.Channels = 2 then
247 begin
248 if Fmt.SampleBits = 16 then
249 Result := AL_FORMAT_STEREO16
250 else
251 Result := AL_FORMAT_STEREO8;
252 end
253 else
254 begin
255 if Fmt.SampleBits = 16 then
256 Result := AL_FORMAT_MONO16
257 else
258 Result := AL_FORMAT_MONO8;
259 end;
260 end;
262 function GetALSourceState(S: ALuint): ALint; inline;
263 begin
264 alGetSourcei(S, AL_SOURCE_STATE, Result);
265 end;
267 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
268 var
269 find_id: DWORD;
270 Loader: TSoundLoader;
271 OutData: Pointer;
272 OutLen: LongWord;
273 begin
274 ID := NO_SOUND_ID;
275 Result := False;
277 find_id := FindESound();
279 e_SoundsArray[find_id].Loader := nil;
280 e_SoundsArray[find_id].isMusic := isMusic;
281 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
282 e_SoundsArray[find_id].nRefs := 0;
284 Loader := e_GetSoundLoader(FileName);
285 if Loader = nil then
286 begin
287 e_LogWritefln('Could not find loader for sound `%s`', [FileName]);
288 exit;
289 end;
291 Loader.Looping := e_SoundsArray[find_id].Loops;
293 if not Loader.Load(FileName, e_SoundsArray[find_id].isMusic) then
294 begin
295 e_LogWritefln('Could not load sound `%s`', [FileName]);
296 exit;
297 end;
299 alGetError(); // reset error state, god damn it
301 if not Loader.Streaming then
302 begin
303 alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
304 if CheckALError() then
305 begin
306 e_LogWritefln('Could not create AL buffer for `%s`: %s', [FileName, GetALError()]);
307 Loader.Free();
308 exit;
309 end;
311 OutLen := Loader.GetAll(OutData);
312 alBufferData(
313 e_SoundsArray[find_id].alBuffer,
314 GetALSoundFormat(Loader.Format),
315 OutData,
316 OutLen,
317 Loader.Format.SampleRate
318 );
320 // don't need this anymore
321 Loader.Free();
322 Loader := nil;
324 if CheckALError() then
325 begin
326 e_LogWriteln('AL: what the fuck: ' + GetALError());
327 alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
328 e_SoundsArray[find_id].alBuffer := 0;
329 exit;
330 end;
331 end
332 else
333 begin
334 e_SoundsArray[find_id].alBuffer := 0;
335 e_SoundsArray[find_id].Loader := Loader;
336 end;
338 ID := find_id;
339 Result := True;
340 end;
342 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
343 var
344 find_id: DWORD;
345 Loader: TSoundLoader;
346 OutData: Pointer;
347 OutLen: LongWord;
348 begin
349 ID := NO_SOUND_ID;
350 Result := False;
352 find_id := FindESound();
354 e_SoundsArray[find_id].Loader := nil;
355 e_SoundsArray[find_id].isMusic := isMusic;
356 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
357 e_SoundsArray[find_id].nRefs := 0;
359 Loader := e_GetSoundLoader(pData, LongWord(Length));
360 if Loader = nil then
361 begin
362 e_LogWritefln('Could not find loader for sound `%p`', [pData]);
363 exit;
364 end;
366 Loader.Looping := e_SoundsArray[find_id].Loops;
368 if not Loader.Load(pData, LongWord(Length), e_SoundsArray[find_id].isMusic) then
369 begin
370 e_LogWritefln('Could not load sound `%p`', [pData]);
371 exit;
372 end;
374 alGetError(); // reset error state, god damn it
376 if not Loader.Streaming then
377 begin
378 alGenBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
379 if CheckALError() then
380 begin
381 e_LogWritefln('Could not create AL buffer for `%p`: %s', [pData, GetALError()]);
382 Loader.Free();
383 exit;
384 end;
386 OutLen := Loader.GetAll(OutData);
387 alBufferData(
388 e_SoundsArray[find_id].alBuffer,
389 GetALSoundFormat(Loader.Format),
390 OutData,
391 OutLen,
392 Loader.Format.SampleRate
393 );
395 // don't need this anymore
396 Loader.Free();
397 Loader := nil;
399 if CheckALError() then
400 begin
401 e_LogWriteln('AL: what the fuck: ' + GetALError());
402 alDeleteBuffers(1, Addr(e_SoundsArray[find_id].alBuffer));
403 e_SoundsArray[find_id].alBuffer := 0;
404 exit;
405 end;
406 end
407 else
408 begin
409 e_SoundsArray[find_id].alBuffer := 0;
410 e_SoundsArray[find_id].Loader := Loader;
411 end;
413 ID := find_id;
414 Result := True;
415 end;
417 function FindSourceForSound(ID: DWORD): Integer;
418 var
419 S: Integer;
420 begin
421 Result := -1;
422 if ID > High(e_SoundsArray) then
423 exit;
425 if e_SoundsArray[ID].Loader <> nil then
426 begin
427 // first source is for streaming sounds
428 // it always exists
429 alOwners[MUSIC_SOURCE] := nil;
430 Result := MUSIC_SOURCE;
431 exit;
432 end;
434 for S := 1 to High(alSources) do
435 if alSources[S] = 0 then
436 begin
437 alOwners[S] := nil; // TBasicSounds will set this if needed
438 Result := S;
439 break;
440 end;
442 if Result = -1 then Exit; // no voices left
444 alGetError(); // reset error state
445 alGenSources(1, @alSources[Result]);
446 if CheckALError() then
447 begin
448 e_LogWriteln('AL: FindSourceForSound(): alGenSources() failed: ' + GetALError());
449 Result := -1;
450 end;
451 end;
453 procedure AssignSound(ID: DWORD; Src: ALuint); inline;
454 begin
455 alGetError(); // reset error state
457 if e_SoundsArray[ID].Loader <> nil then
458 begin
459 // this is a stream
460 // reset position
461 e_SoundsArray[ID].Loader.SetPosition(0);
462 if CurStream <> ID then // changing streams, stop the thing just in case
463 alSourceStop(Src);
464 // this shit is playing now
465 CurStream := ID;
466 end
467 else
468 begin
469 // this is a full chunk, assign local buffer
470 alSourcei(Src, AL_BUFFER, e_SoundsArray[ID].alBuffer);
471 // these can loop
472 if (e_SoundsArray[ID].Loops) then
473 alSourcei(Src, AL_LOOPING, AL_TRUE)
474 else
475 alSourcei(Src, AL_LOOPING, AL_FALSE);
476 end;
478 alSourcei(Src, AL_SOURCE_RELATIVE, AL_TRUE);
479 end;
481 function e_PlaySound(ID: DWORD): Integer;
482 begin
483 Result := FindSourceForSound(ID);
484 if Result >= 0 then
485 begin
486 AssignSound(ID, alSources[Result]);
487 alSourcef(alSources[Result], AL_GAIN, 1);
488 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
489 alSourcePlay(alSources[Result]);
490 end;
491 end;
493 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
494 var
495 Pos: array [0..2] of ALfloat;
496 begin
497 Result := FindSourceForSound(ID);
498 if Result >= 0 then
499 begin
500 Pos[0] := Pan;
501 Pos[1] := 0;
502 Pos[2] := 0;
503 AssignSound(ID, alSources[Result]);
504 alSourcef(alSources[Result], AL_GAIN, 1);
505 alSourcefv(alSources[Result], AL_POSITION, Pos);
506 alSourcePlay(alSources[Result]);
507 end;
508 end;
510 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
511 begin
512 Result := FindSourceForSound(ID);
513 if Result >= 0 then
514 begin
515 AssignSound(ID, alSources[Result]);
516 alSourcef(alSources[Result], AL_GAIN, Volume);
517 alSourcefv(alSources[Result], AL_POSITION, e_ZeroPosition);
518 alSourcePlay(alSources[Result]);
519 end;
520 end;
522 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
523 var
524 Pos: array [0..2] of ALfloat;
525 begin
526 Result := FindSourceForSound(ID);
527 if Result >= 0 then
528 begin
529 Pos[0] := Pan;
530 Pos[1] := 0;
531 Pos[2] := 0;
532 AssignSound(ID, alSources[Result]);
533 alSourcefv(alSources[Result], AL_POSITION, Pos);
534 alSourcef(alSources[Result], AL_GAIN, Volume);
535 alSourcePlay(alSources[Result]);
536 end;
537 end;
539 procedure e_DeleteSound(ID: DWORD);
540 begin
541 if ID > High(e_SoundsArray) then
542 exit;
543 if (e_SoundsArray[ID].alBuffer <> 0) then
544 begin
545 alDeleteBuffers(1, Addr(e_SoundsArray[ID].alBuffer));
546 e_SoundsArray[ID].alBuffer := 0;
547 end;
548 if (e_SoundsArray[ID].Loader <> nil) then
549 begin
550 e_SoundsArray[ID].Loader.Free();
551 e_SoundsArray[ID].Loader := nil;
552 if ID = CurStream then
553 CurStream := NO_SOUND_ID;
554 end;
555 end;
557 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
558 var
559 S: Integer;
560 V: ALfloat;
561 begin
562 // TODO: replace manual volume calculations everywhere with
563 // alListenerf(AL_GAIN) or something
564 if setMode then
565 begin
566 for S := 1 to High(alSources) do
567 if alSources[S] <> 0 then
568 alSourcef(alSources[S], AL_GAIN, SoundMod)
569 end
570 else
571 begin
572 for S := 1 to High(alSources) do
573 if alSources[S] <> 0 then
574 begin
575 alGetSourcef(alSources[S], AL_GAIN, V);
576 alSourcef(alSources[S], AL_GAIN, V * SoundMod);
577 end;
578 end;
579 end;
581 procedure e_MuteChannels(Enable: Boolean);
582 begin
583 if Enable = SoundMuted then
584 Exit;
586 SoundMuted := Enable;
587 end;
589 procedure e_StopChannels();
590 var
591 S: Integer;
592 begin
593 alGetError(); // reset error state
594 for S := Low(alSources) to High(alSources) do
595 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_PLAYING) then
596 begin
597 alSourceStop(alSources[S]);
598 alDeleteSources(1, @alSources[S]);
599 alSources[S] := 0;
600 end;
601 end;
603 procedure e_RemoveAllSounds();
604 var
605 i: Integer;
606 begin
607 for i := 0 to High(e_SoundsArray) do
608 if e_SoundsArray[i].alBuffer <> 0 then
609 e_DeleteSound(i);
610 SetLength(e_SoundsArray, 0);
611 e_SoundsArray := nil;
612 CurStream := NO_SOUND_ID;
613 end;
615 procedure e_ReleaseSoundSystem();
616 begin
617 e_RemoveAllSounds();
619 alcMakeContextCurrent(nil);
620 alcDestroyContext(alContext);
621 alcCloseDevice(alDevice);
623 alDevice := nil;
624 alContext := nil;
625 end;
627 procedure UpdateStreamSource(Src: Integer);
628 var
629 OutLen: LongWord;
630 Buf: ALuint;
631 S: Integer;
632 begin
633 if alSources[Src] = 0 then Exit;
635 alGetError(); // reset error state
637 alGetSourcei(alSources[Src], AL_BUFFERS_PROCESSED, S);
638 // unqueue processed buffers
639 if S > 0 then
640 begin
641 alSourceUnqueueBuffers(alSources[Src], S, @alStreamBufs[alStreamAvail]);
642 alStreamAvail := alStreamAvail + S;
643 end;
645 alGetError(); // reset error state
647 if (alStreamAvail > 0) and (CurStream <> NO_SOUND_ID) then
648 begin
649 // some buffers have freed up, advance stream playback
650 OutLen := e_SoundsArray[CurStream].Loader.FillBuffer(@alStreamData[0], STREAM_BUFSIZE);
651 if OutLen = 0 then Exit; // ran out of stream
652 Buf := alStreamBufs[alStreamAvail-1];
653 Dec(alStreamAvail);
654 // upload
655 alBufferData(
656 Buf,
657 GetALSoundFormat(e_SoundsArray[CurStream].Loader.Format),
658 @alStreamData[0],
659 OutLen,
660 e_SoundsArray[CurStream].Loader.Format.SampleRate
661 );
662 // attach
663 alSourceQueueBuffers(alSources[Src], 1, @Buf);
664 // restart if needed
665 S := GetALSourceState(alSources[Src]);
666 if (S = AL_STOPPED) or (S = AL_INITIAL) then
667 alSourcePlay(alSources[Src]);
668 end;
669 end;
671 procedure e_SoundUpdate();
672 var
673 S: Integer;
674 begin
675 alGetError(); // reset error state
677 // clear out all stopped sources
678 for S := 1 to High(alSources) do
679 if (alSources[S] <> 0) and (GetALSourceState(alSources[S]) = AL_STOPPED) then
680 begin
681 alDeleteSources(1, @alSources[S]);
682 alSources[S] := 0;
683 alOwners[S] := nil;
684 end;
686 // update the stream sources
687 UpdateStreamSource(MUSIC_SOURCE);
688 end;
690 { TBasicSound: }
692 constructor TBasicSound.Create();
693 begin
694 FID := NO_SOUND_ID;
695 FMusic := False;
696 FSource := -1;
697 FPosition := 0;
698 FMuted := False;
699 FOldGain := 1;
700 end;
702 destructor TBasicSound.Destroy();
703 begin
704 FreeSound();
705 inherited;
706 end;
708 function TBasicSound.InvalidSource(): Boolean; inline;
709 begin
710 Result := (FSource < 0) or (alSources[FSource] = 0) or (alOwners[FSource] <> self);
711 end;
713 procedure TBasicSound.FreeSound();
714 begin
715 if FID = NO_SOUND_ID then
716 Exit;
718 Stop();
719 FID := NO_SOUND_ID;
720 FMusic := False;
721 FPosition := 0;
722 end;
724 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
725 begin
726 Result := False;
727 if FID = NO_SOUND_ID then Exit;
729 if e_SoundsArray[FID].nRefs >= gMaxSimSounds then
730 begin
731 Result := True;
732 Exit;
733 end;
735 FSource := e_PlaySoundPanVolume(FID, Pan, Volume);
736 if FSource >= 0 then
737 begin
738 alOwners[FSource] := self;
739 Result := True;
740 end;
741 end;
743 procedure TBasicSound.SetID(ID: DWORD);
744 begin
745 FreeSound();
747 if ID > High(e_SoundsArray) then
748 exit;
750 FID := ID;
751 FMusic := e_SoundsArray[ID].isMusic;
752 end;
754 function TBasicSound.IsPlaying(): Boolean;
755 begin
756 Result := False;
757 if InvalidSource() then
758 Exit;
759 Result := GetALSourceState(alSources[FSource]) = AL_PLAYING;
760 end;
762 procedure TBasicSound.Stop();
763 begin
764 if FID = CurStream then
765 CurStream := NO_SOUND_ID;
766 if InvalidSource() then
767 Exit;
768 GetPosition();
769 alSourceStop(alSources[FSource]);
770 end;
772 function TBasicSound.IsPaused(): Boolean;
773 begin
774 Result := False;
775 if InvalidSource() then
776 Exit;
777 Result := GetALSourceState(alSources[FSource]) = AL_PAUSED;
778 end;
780 procedure TBasicSound.Pause(Enable: Boolean);
781 begin
782 if InvalidSource() then
783 Exit;
784 if Enable then
785 alSourcePause(alSources[FSource])
786 else
787 alSourcePlay(alSources[FSource]);
788 end;
790 function TBasicSound.GetVolume(): Single;
791 begin
792 Result := 0.0;
793 if InvalidSource() then
794 Exit;
795 alGetSourcef(alSources[FSource], AL_GAIN, Result);
796 end;
798 procedure TBasicSound.SetVolume(Volume: Single);
799 begin
800 if InvalidSource() then
801 Exit;
802 alSourcef(alSources[FSource], AL_GAIN, Volume);
803 end;
805 function TBasicSound.GetPan(): Single;
806 var
807 Pos: array [0..2] of ALfloat;
808 begin
809 Result := 0.0;
810 if InvalidSource() then
811 Exit;
812 alGetSourcefv(alSources[FSource], AL_POSITION, Pos);
813 Result := Pos[0];
814 end;
816 procedure TBasicSound.SetPan(Pan: Single);
817 var
818 Pos: array [0..2] of ALfloat;
819 begin
820 if InvalidSource() then
821 Exit;
822 Pos[0] := Pan;
823 Pos[1] := 0;
824 Pos[2] := 0;
825 alSourcefv(alSources[FSource], AL_POSITION, Pos);
826 end;
828 function TBasicSound.IsMuted(): Boolean;
829 begin
830 if InvalidSource() then
831 Result := False
832 else
833 Result := FMuted;
834 end;
836 procedure TBasicSound.Mute(Enable: Boolean);
837 begin
838 if InvalidSource() then
839 Exit;
840 if Enable then
841 begin
842 FOldGain := GetVolume();
843 FMuted := True;
844 SetVolume(0);
845 end
846 else if FMuted then
847 begin
848 FMuted := False;
849 SetVolume(FOldGain);
850 end;
851 end;
853 function TBasicSound.GetPosition(): DWORD;
854 var
855 Bytes: ALint;
856 begin
857 Result := 0;
858 if InvalidSource() then
859 Exit;
860 alGetSourcei(alSources[FSource], AL_BYTE_OFFSET, Bytes);
861 FPosition := Bytes;
862 Result := FPosition;
863 end;
865 procedure TBasicSound.SetPosition(aPos: DWORD);
866 begin
867 FPosition := aPos;
868 if InvalidSource() then
869 Exit;
870 alSourcei(alSources[FSource], AL_BYTE_OFFSET, aPos);
871 end;
873 procedure TBasicSound.SetPriority(priority: Integer);
874 begin
875 end;
877 end.