DEADSOFTWARE

35cd2c34430ed5db0aa0190b2276ee605c82a4f5
[d2df-sdl.git] / src / engine / e_sound_sdl.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 sdl2,
19 SDL2_mixer,
20 {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
21 e_log,
22 SysUtils;
24 type
25 TSoundRec = record
26 Data: Pointer;
27 Sound: PMix_Chunk;
28 Music: PMix_Music;
29 isMusic: Boolean;
30 Loops: Boolean;
31 nRefs: Integer;
32 end;
34 TBasicSound = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
35 private
36 FChanNum: Integer; // <0: no channel allocated
38 protected
39 FID: DWORD;
40 FMusic: Boolean;
41 FPosition: DWORD;
42 FPriority: Integer;
44 function RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
45 function GetChan (): Integer;
47 property Channel: Integer read GetChan;
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 // returns channel number or -1
78 function e_PlaySound(ID: DWORD): Integer;
79 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
80 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
81 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
83 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
84 procedure e_MuteChannels(Enable: Boolean);
85 procedure e_StopChannels();
87 procedure e_DeleteSound(ID: DWORD);
88 procedure e_RemoveAllSounds();
89 procedure e_ReleaseSoundSystem();
90 procedure e_SoundUpdate();
92 var
93 e_SoundsArray: array of TSoundRec = nil;
95 implementation
97 uses
98 g_window, g_options;
100 const
101 N_CHANNELS = 512;
102 N_MUSCHAN = N_CHANNELS+42;
104 type
105 TChanInfo = record
106 id: DWORD; // sound id
107 muted: Boolean;
108 oldvol: Integer; // for muted
109 pan: Single;
110 end;
112 var
113 SoundMuted: Boolean = False;
114 SoundInitialized: Boolean = False;
115 ChanSIds: array[0..N_CHANNELS] of TChanInfo;
116 MusVolume: Integer = MIX_MAX_VOLUME;
119 procedure chanFinished (chan: Integer); cdecl;
120 begin
121 //e_WriteLog(Format('chanFinished: %d', [chan]), TMsgType.Notify);
122 if (chan >= 0) and (chan < N_CHANNELS) then
123 begin
124 if ChanSIds[chan].id <> NO_SOUND_ID then
125 begin
126 if (ChanSIds[chan].id <= High(e_SoundsArray)) and (e_SoundsArray[ChanSIds[chan].id].nRefs > 0) then
127 begin
128 Dec(e_SoundsArray[ChanSIds[chan].id].nRefs);
129 end;
130 ChanSIds[chan].id := NO_SOUND_ID;
131 end;
132 end;
133 end;
136 procedure dumpMusicType (ms: PMix_Music);
137 begin
138 if ms = nil then
139 begin
140 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
141 end
142 else
143 begin
144 case Mix_GetMusicType(ms^) of
145 TMix_MusicType.MUS_NONE:
146 e_WriteLog('MUSIC FORMAT: NONE', TMsgType.Notify);
147 TMix_MusicType.MUS_CMD:
148 e_WriteLog('MUSIC FORMAT: CMD', TMsgType.Notify);
149 TMix_MusicType.MUS_WAV:
150 e_WriteLog('MUSIC FORMAT: WAV', TMsgType.Notify);
151 TMix_MusicType.MUS_MOD:
152 e_WriteLog('MUSIC FORMAT: MOD', TMsgType.Notify);
153 TMix_MusicType.MUS_MID:
154 e_WriteLog('MUSIC FORMAT: MID', TMsgType.Notify);
155 TMix_MusicType.MUS_OGG:
156 e_WriteLog('MUSIC FORMAT: OGG', TMsgType.Notify);
157 TMix_MusicType.MUS_MP3:
158 e_WriteLog('MUSIC FORMAT: MP3', TMsgType.Notify);
159 TMix_MusicType.MUS_MP3_MAD:
160 e_WriteLog('MUSIC FORMAT: MP3_MAD', TMsgType.Notify);
161 TMix_MusicType.MUS_FLAC:
162 e_WriteLog('MUSIC FORMAT: FLAC', TMsgType.Notify);
163 TMix_MusicType.MUS_MODPLUG:
164 e_WriteLog('MUSIC FORMAT: MODPLUG', TMsgType.Notify);
165 otherwise
166 e_WriteLog('MUSIC FORMAT: UNKNOWN', TMsgType.Notify);
167 end;
168 end;
169 end;
171 function e_InitSoundSystem(NoOutput: Boolean = False): Boolean;
172 var
173 res, i: Integer;
174 rfreq: Integer;
175 rformat: UInt16;
176 rchans: Integer;
177 begin
178 if SoundInitialized then begin Result := true; Exit end;
180 Result := False;
181 SoundInitialized := False;
183 if NoOutput then begin Result := true; Exit end;
185 // wow, this is actually MIDI player!
186 // we need module player
187 res := Mix_Init(MIX_INIT_FLAC or MIX_INIT_MOD or MIX_INIT_MODPLUG or MIX_INIT_MP3 or MIX_INIT_OGG or MIX_INIT_FLUIDSYNTH);
188 e_WriteLog(Format('SDL: res=0x%x', [res]), TMsgType.Notify);
189 if (res and MIX_INIT_FLAC) <> 0 then e_WriteLog('SDL: FLAC playback is active', TMsgType.Notify);
190 if (res and MIX_INIT_MOD) <> 0 then e_WriteLog('SDL: MOD playback is active', TMsgType.Notify);
191 if (res and MIX_INIT_MODPLUG) <> 0 then e_WriteLog('SDL: MODPLUG playback is active', TMsgType.Notify);
192 if (res and MIX_INIT_MP3) <> 0 then e_WriteLog('SDL: MP3 playback is active', TMsgType.Notify);
193 if (res and MIX_INIT_OGG) <> 0 then e_WriteLog('SDL: OGG playback is active', TMsgType.Notify);
194 if (res and MIX_INIT_FLUIDSYNTH) <> 0 then e_WriteLog('SDL: FLUIDSYNTH playback is active', TMsgType.Notify);
196 e_WriteLog(Format('SDL: initializing mixer at %d with buffer %d', [gsSDLSampleRate, gsSDLBufferSize]), TMsgType.Notify);
197 res := Mix_OpenAudio(gsSDLSampleRate, AUDIO_S16LSB, 2, gsSDLBufferSize);
198 if res = -1 then
199 begin
200 e_WriteLog('Error initializing SDL mixer:', TMsgType.Fatal);
201 e_WriteLog(Mix_GetError(), TMsgType.Fatal);
202 Exit;
203 end;
205 if Mix_QuerySpec(@rfreq, @rformat, @rchans) > 0 then
206 begin
207 e_WriteLog(Format('SDL: frequency=%d; format=%u; channels=%d', [rfreq, rformat, rchans]), TMsgType.Notify);
208 end;
210 for i := 0 to Mix_GetNumChunkDecoders()-1 do
211 begin
212 e_WriteLog(Format('SDL: chunk decoder %s is avalable', [Mix_GetChunkDecoder(i)]), TMsgType.Notify);
213 end;
214 for i := 0 to Mix_GetNumMusicDecoders()-1 do
215 begin
216 e_WriteLog(Format('SDL: music decoder %s is avalable', [Mix_GetMusicDecoder(i)]), TMsgType.Notify);
217 end;
219 Mix_AllocateChannels(N_CHANNELS);
220 Mix_ChannelFinished(chanFinished);
222 for i := 0 to N_CHANNELS-1 do
223 begin
224 ChanSIds[i].id := NO_SOUND_ID;
225 ChanSIds[i].muted := SoundMuted;
226 ChanSIds[i].oldvol := MIX_MAX_VOLUME;
227 ChanSIds[i].pan := 1.0;
228 end;
229 MusVolume := MIX_MAX_VOLUME;
231 SoundInitialized := True;
232 Result := True;
233 end;
235 function e_isMusic (id: DWORD): Boolean;
236 begin
237 Result := False;
238 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
239 begin
240 Result := (e_SoundsArray[id].Music <> nil);
241 end;
242 end;
244 function e_isSound (id: DWORD): Boolean;
245 begin
246 Result := False;
247 if (e_SoundsArray <> nil) and (id <= High(e_SoundsArray)) then
248 begin
249 Result := (e_SoundsArray[id].Sound <> nil);
250 end;
251 end;
253 function FindESound(): DWORD;
254 var
255 i: Integer;
256 begin
257 if e_SoundsArray <> nil then
258 begin
259 for i := 0 to High(e_SoundsArray) do
260 if (e_SoundsArray[i].Sound = nil) and (e_SoundsArray[i].Music = nil) then
261 begin
262 Result := i;
263 Exit;
264 end;
265 end;
266 if e_SoundsArray = nil then
267 begin
268 SetLength(e_SoundsArray, 16);
269 Result := 0;
270 end
271 else
272 begin
273 Result := High(e_SoundsArray) + 1;
274 SetLength(e_SoundsArray, Length(e_SoundsArray) + 16);
275 end;
276 for i := Result to High(e_SoundsArray) do
277 begin
278 e_SoundsArray[i].Sound := nil;
279 e_SoundsArray[i].Music := nil;
280 e_SoundsArray[i].Data := nil;
281 e_SoundsArray[i].isMusic := False;
282 e_SoundsArray[i].nRefs := 0;
283 end;
284 end;
286 function e_LoadSound(FileName: String; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
287 var
288 find_id: DWORD;
289 begin
290 ID := NO_SOUND_ID;
291 Result := False;
292 if not SoundInitialized then Exit;
294 if isMusic then e_WriteLog('Loading music '+FileName+'...', TMsgType.Notify)
295 else e_WriteLog('Loading sound '+FileName+'...', TMsgType.Notify);
298 if isMusic then
299 begin
300 e_WriteLog('IGNORING MUSIC FROM FILE', TMsgType.Warning);
301 Exit;
302 end;
305 find_id := FindESound();
307 e_SoundsArray[find_id].Data := nil;
308 e_SoundsArray[find_id].isMusic := isMusic;
309 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
310 e_SoundsArray[find_id].nRefs := 0;
312 if isMusic then
313 begin
314 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
315 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
316 if e_SoundsArray[find_id].Music = nil then
317 begin
318 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
319 e_WriteLog(Mix_GetError(), TMsgType.Warning);
320 Exit;
321 end;
322 dumpMusicType(e_SoundsArray[find_id].Music);
323 end
324 else
325 begin
326 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
327 if e_SoundsArray[find_id].Sound = nil then Exit;
328 end;
330 ID := find_id;
332 Result := True;
333 end;
335 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean; ForceNoLoop: Boolean = False): Boolean;
336 var
337 find_id: DWORD;
338 rw: PSDL_RWops;
339 //pc: PChar;
340 isid3: Boolean;
341 begin
342 ID := NO_SOUND_ID;
343 Result := False;
344 if not SoundInitialized then Exit;
345 isid3 := False;
348 if isMusic then
349 begin
350 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
351 Exit;
352 end;
355 //FIXME: correctly skip ID3
357 pc := PChar(pData);
358 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
359 begin
360 isid3 := True;
361 Inc(pc, $400);
362 pData := Pointer(pc);
363 Dec(Length, $400);
364 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
365 end;
368 rw := SDL_RWFromConstMem(pData, Length);
369 if rw = nil then Exit;
371 find_id := FindESound();
373 e_SoundsArray[find_id].Data := pData;
374 if isid3 then e_SoundsArray[find_id].Data := nil;
375 e_SoundsArray[find_id].isMusic := isMusic;
376 e_SoundsArray[find_id].Loops := isMusic and not ForceNoLoop;
377 e_SoundsArray[find_id].nRefs := 0;
379 if isMusic then
380 begin
381 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
382 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
383 if e_SoundsArray[find_id].Music = nil then
384 begin
385 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
386 e_WriteLog(Mix_GetError(), TMsgType.Warning);
387 end
388 else
389 begin
390 dumpMusicType(e_SoundsArray[find_id].Music);
391 end;
392 //SDL_FreeRW(rw);
394 if e_SoundsArray[find_id].Music <> nil then
395 begin
396 Mix_FreeMusic(e_SoundsArray[find_id].Music);
397 end;
398 e_SoundsArray[find_id].Music := nil;
399 Exit;
401 end
402 else
403 begin
404 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
405 end;
406 //SDL_FreeRW(rw); // somehow it segfaults...
407 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
408 begin
409 e_SoundsArray[find_id].Data := nil;
410 Exit;
411 end;
413 ID := find_id;
415 Result := True;
416 end;
418 function e_PlaySound (ID: DWORD): Integer;
419 var
420 res: Integer = -1;
421 loops: Integer = 0;
422 begin
423 Result := -1;
424 if not SoundInitialized then Exit;
426 if e_isSound(ID) then
427 begin
428 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
429 Inc(e_SoundsArray[ID].nRefs);
430 if e_SoundsArray[ID].Loops then loops := -1;
431 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, loops);
432 if res >= 0 then
433 begin
434 ChanSIds[res].id := ID;
435 ChanSIds[res].muted := SoundMuted;
436 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
438 if e_SoundsArray[ID].isMusic then
439 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
440 else
441 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
443 end;
444 end
445 else
446 begin
447 if not e_isMusic(ID) then Exit;
448 Mix_HaltMusic();
449 if e_SoundsArray[ID].Loops then loops := -1;
450 res := Mix_PlayMusic(e_SoundsArray[ID].Music, loops);
451 if res >= 0 then res := N_MUSCHAN;
452 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
453 Result := res;
454 end;
456 Result := res;
457 end;
459 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
460 var
461 l, r: UInt8;
462 begin
463 Result := True;
464 if chan = N_MUSCHAN then
465 begin
466 // no panning for music
467 end
468 else if chan >= 0 then
469 begin
470 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
471 Pan := Pan+1.0; // 0..2
472 l := trunc(127.0*(2.0-Pan));
473 r := trunc(127.0*Pan);
474 Mix_SetPanning(chan, l, r);
475 ChanSIds[chan].pan := Pan;
476 end
477 else
478 begin
479 Result := False;
480 end;
481 end;
483 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
484 var
485 vol: Integer;
486 begin
487 Result := True;
488 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
489 vol := trunc(Volume*MIX_MAX_VOLUME);
490 if chan = N_MUSCHAN then
491 begin
492 MusVolume := vol;
493 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
494 end
495 else if chan >= 0 then
496 begin
497 ChanSIds[chan].oldvol := vol;
498 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
499 end
500 else
501 begin
502 Result := False;
503 end;
504 end;
506 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
507 var
508 chan: Integer;
509 begin
510 Result := -1;
511 chan := e_PlaySound(ID);
512 e_chanSetPan(chan, Pan);
513 Result := chan;
514 end;
516 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
517 var
518 chan: Integer;
519 begin
520 Result := -1;
521 chan := e_PlaySound(ID);
522 e_chanSetVol(chan, Volume);
523 Result := chan;
524 end;
526 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
527 var
528 chan: Integer;
529 begin
530 Result := -1;
531 chan := e_PlaySound(ID);
532 e_chanSetPan(chan, Pan);
533 e_chanSetVol(chan, Volume);
534 Result := chan;
535 end;
537 procedure e_DeleteSound(ID: DWORD);
538 var
539 i: Integer;
540 begin
541 if ID > High(e_SoundsArray) then Exit;
542 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
544 for i := 0 to N_CHANNELS-1 do
545 begin
546 if ChanSIds[i].id = ID then
547 begin
548 ChanSIds[i].id := NO_SOUND_ID;
549 Mix_HaltChannel(i);
550 end;
551 end;
553 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
554 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
555 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
557 e_SoundsArray[ID].Sound := nil;
558 e_SoundsArray[ID].Music := nil;
559 e_SoundsArray[ID].Data := nil;
560 e_SoundsArray[ID].nRefs := 0;
561 end;
563 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
564 var
565 i: Integer;
566 vol: Single;
567 ovol: Integer;
568 begin
569 for i := 0 to N_CHANNELS-1 do
570 begin
571 ovol := ChanSIds[i].oldvol;
572 if setMode then
573 begin
574 vol := SoundMod;
575 end
576 else
577 begin
578 vol := (MIX_MAX_VOLUME+0.0)/ovol;
579 vol := vol*SoundMod;
580 end;
581 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
582 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
583 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
584 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
585 end;
586 ovol := Mix_VolumeMusic(-1);
587 if ovol >= 0 then
588 begin
589 if setMode then
590 begin
591 vol := SoundMod;
592 end
593 else
594 begin
595 vol := (MIX_MAX_VOLUME+0.0)/ovol;
596 vol := vol * SoundMod;
597 end;
598 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
599 MusVolume := trunc(vol*MIX_MAX_VOLUME);
600 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
601 end;
602 end;
604 procedure e_MuteChannels(Enable: Boolean);
605 var
606 i: Integer;
607 begin
608 //if Enable = SoundMuted then Exit;
609 SoundMuted := Enable;
610 for i := 0 to N_CHANNELS-1 do
611 begin
612 if ChanSIds[i].muted <> SoundMuted then
613 begin
614 ChanSIds[i].muted := SoundMuted;
615 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
616 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
617 end;
618 end;
619 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
620 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
621 end;
623 procedure e_StopChannels();
624 var
625 i: Integer;
626 begin
627 Mix_HaltChannel(-1);
628 Mix_HaltMusic();
629 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
630 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
631 end;
633 procedure e_RemoveAllSounds();
634 var
635 i: Integer;
636 begin
637 if SoundInitialized then e_StopChannels();
638 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
639 SetLength(e_SoundsArray, 0);
640 e_SoundsArray := nil;
641 end;
643 procedure e_ReleaseSoundSystem();
644 begin
645 e_RemoveAllSounds();
646 if SoundInitialized then
647 begin
648 Mix_CloseAudio();
649 SoundInitialized := False;
650 end;
651 end;
653 procedure e_SoundUpdate();
654 begin
655 //FMOD_System_Update(F_System);
656 end;
659 { TBasicSound: }
661 constructor TBasicSound.Create();
662 begin
663 FID := NO_SOUND_ID;
664 FMusic := False;
665 FChanNum := -1;
666 FPosition := 0;
667 FPriority := 128;
668 end;
670 destructor TBasicSound.Destroy();
671 begin
672 FreeSound();
673 inherited;
674 end;
676 function TBasicSound.GetChan (): Integer;
677 begin
678 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
679 begin
680 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
681 end
682 else if e_isMusic(FID) then
683 begin
684 FChanNum := N_MUSCHAN;
685 end;
686 Result := FChanNum;
687 end;
689 procedure TBasicSound.FreeSound();
690 begin
691 if FID = NO_SOUND_ID then Exit;
692 Stop();
693 FID := NO_SOUND_ID;
694 FMusic := False;
695 FPosition := 0;
696 FChanNum := -1;
697 end;
699 // aPos: msecs
700 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
701 begin
702 Result := False;
703 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
704 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
705 Result := (FChanNum >= 0);
706 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
707 //TODO: aPos
708 end;
710 procedure TBasicSound.SetID(ID: DWORD);
711 begin
712 FreeSound();
713 FID := ID;
714 if ID <> NO_SOUND_ID then
715 begin
716 FMusic := e_SoundsArray[ID].isMusic;
717 end;
718 FChanNum := -1;
719 end;
721 function TBasicSound.IsPlaying(): Boolean;
722 var
723 chan: Integer;
724 begin
725 Result := False;
726 if e_isSound(FID) then
727 begin
728 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
729 chan := Channel;
730 if chan < 0 then
731 begin
732 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
733 Exit;
734 end;
735 //Result := (Mix_Playing(chan) > 0)
736 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
737 Result := True;
738 end
739 else if e_isMusic(FID) then
740 begin
741 Result := (Mix_PlayingMusic() > 0);
742 end;
743 end;
745 procedure TBasicSound.Stop();
746 var
747 chan: Integer;
748 begin
749 if e_isSound(FID) then
750 begin
751 chan := Channel;
752 if chan >= 0 then
753 begin
754 //GetPosition();
755 Mix_HaltChannel(chan);
756 end;
757 end
758 else if e_isMusic(FID) then
759 begin
760 Mix_HaltMusic();
761 end;
762 FChanNum := -1;
763 end;
765 function TBasicSound.IsPaused(): Boolean;
766 var
767 chan: Integer;
768 begin
769 Result := False;
770 if e_isSound(FID) then
771 begin
772 chan := Channel;
773 if chan < 0 then Exit;
774 Result := (Mix_Paused(chan) > 0);
775 end
776 else if e_isMusic(FID) then
777 begin
778 Result := (Mix_PausedMusic() > 0);
779 end;
780 end;
782 procedure TBasicSound.Pause(Enable: Boolean);
783 var
784 chan: Integer;
785 pl: Boolean;
786 begin
787 Enable := not Enable; // fuckin' double negation
788 if e_isSound(FID) then
789 begin
790 chan := Channel;
791 if chan < 0 then Exit;
792 pl := not (Mix_Paused(chan) > 0);
793 if pl <> Enable then
794 begin
795 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
796 end;
797 end
798 else if e_isMusic(FID) then
799 begin
800 pl := not (Mix_PausedMusic() > 0);
801 if pl <> Enable then
802 begin
803 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
804 end;
805 end;
807 if Enable then
808 begin
809 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
810 if res <> FMOD_OK then
811 begin
812 end;
813 end;
815 end;
817 function TBasicSound.GetVolume(): Single;
818 var
819 chan: Integer;
820 begin
821 Result := 0.0;
822 if e_isSound(FID) then
823 begin
824 chan := Channel;
825 if chan < 0 then Exit;
826 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
827 end
828 else if e_isMusic(FID) then
829 begin
830 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
831 end;
832 end;
834 procedure TBasicSound.SetVolume(Volume: Single);
835 var
836 chan: Integer;
837 begin
838 if e_isSound(FID) then
839 begin
840 chan := Channel;
841 if chan < 0 then Exit;
842 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
843 e_chanSetVol(chan, Volume);
844 end
845 else if e_isMusic(FID) then
846 begin
847 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
848 e_chanSetVol(N_MUSCHAN, Volume);
849 end;
850 end;
852 function TBasicSound.GetPan(): Single;
853 var
854 chan: Integer;
855 begin
856 Result := 1.0;
857 if e_isSound(FID) then
858 begin
859 chan := Channel;
860 if chan < 0 then Exit;
861 Result := ChanSIds[chan].pan;
862 end;
863 end;
865 procedure TBasicSound.SetPan(Pan: Single);
866 var
867 chan: Integer;
868 begin
869 if e_isSound(FID) then
870 begin
871 chan := Channel;
872 if chan < 0 then Exit;
873 e_chanSetPan(chan, Pan);
874 end;
875 end;
877 function TBasicSound.IsMuted(): Boolean;
878 var
879 chan: Integer;
880 begin
881 Result := False;
882 if e_isSound(FID) then
883 begin
884 chan := Channel;
885 if chan < 0 then Exit;
886 Result := ChanSIds[chan].muted;
887 end
888 else if e_isMusic(FID) then
889 begin
890 Result := SoundMuted;
891 end;
892 end;
894 procedure TBasicSound.Mute(Enable: Boolean);
895 var
896 chan: Integer;
897 begin
898 if e_isSound(FID) then
899 begin
900 chan := Channel;
901 if chan < 0 then Exit;
902 if ChanSIds[chan].muted <> Enable then
903 begin
904 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
905 ChanSIds[chan].muted := Enable;
906 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
907 end;
908 end
909 else if e_isMusic(FID) then
910 begin
911 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
912 end;
913 end;
915 //TODO
916 function TBasicSound.GetPosition(): DWORD;
917 begin
918 Result := 0;
920 if FChanNum < 0 then Exit;
921 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
922 if res <> FMOD_OK then
923 begin
924 Exit;
925 end;
926 Result := FPosition;
928 end;
930 //TODO
931 procedure TBasicSound.SetPosition(aPos: DWORD);
932 begin
933 FPosition := aPos;
935 if FChanNum < 0 then Exit;
936 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
937 if res <> FMOD_OK then
938 begin
939 end;
941 end;
943 //TODO
944 procedure TBasicSound.SetPriority(priority: Integer);
945 begin
947 if (FChanNum <> nil) and (FPriority <> priority) and
948 (priority >= 0) and (priority <= 256) then
949 begin
950 FPriority := priority;
951 res := FMOD_Channel_SetPriority(FChanNum, priority);
952 if res <> FMOD_OK then
953 begin
954 end;
955 end;
957 end;
959 end.