DEADSOFTWARE

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