DEADSOFTWARE

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