DEADSOFTWARE

ade82620b1dd0bec1c121a52b7e43ed28b787428
[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 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): Boolean;
75 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean): 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): 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].nRefs := 0;
311 if isMusic then
312 begin
313 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
314 e_SoundsArray[find_id].Music := Mix_LoadMUS(PAnsiChar(FileName));
315 if e_SoundsArray[find_id].Music = nil then
316 begin
317 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
318 e_WriteLog(Mix_GetError(), TMsgType.Warning);
319 Exit;
320 end;
321 dumpMusicType(e_SoundsArray[find_id].Music);
322 end
323 else
324 begin
325 e_SoundsArray[find_id].Sound := Mix_LoadWAV(PAnsiChar(FileName));
326 if e_SoundsArray[find_id].Sound = nil then Exit;
327 end;
329 ID := find_id;
331 Result := True;
332 end;
334 function e_LoadSoundMem(pData: Pointer; Length: Integer; var ID: DWORD; isMusic: Boolean): Boolean;
335 var
336 find_id: DWORD;
337 rw: PSDL_RWops;
338 //pc: PChar;
339 isid3: Boolean;
340 begin
341 ID := NO_SOUND_ID;
342 Result := False;
343 if not SoundInitialized then Exit;
344 isid3 := False;
347 if isMusic then
348 begin
349 e_WriteLog('IGNORING MUSIC FROM MEMORY', TMsgType.Warning);
350 Exit;
351 end;
354 //FIXME: correctly skip ID3
356 pc := PChar(pData);
357 if (Length > $400) and (pc[0] = 'I') and (pc[1] = 'D') and (pc[2] = '3') then
358 begin
359 isid3 := True;
360 Inc(pc, $400);
361 pData := Pointer(pc);
362 Dec(Length, $400);
363 e_WriteLog('MUSIC: MP3 ID3 WORKAROUND APPLIED!', TMsgType.Warning);
364 end;
367 rw := SDL_RWFromConstMem(pData, Length);
368 if rw = nil then Exit;
370 find_id := FindESound();
372 e_SoundsArray[find_id].Data := pData;
373 if isid3 then e_SoundsArray[find_id].Data := nil;
374 e_SoundsArray[find_id].isMusic := isMusic;
375 e_SoundsArray[find_id].nRefs := 0;
377 if isMusic then
378 begin
379 e_WriteLog(Format(' MUSIC SLOT: %u', [find_id]), TMsgType.Notify);
380 e_SoundsArray[find_id].Music := Mix_LoadMUS_RW(rw, 0);
381 if e_SoundsArray[find_id].Music = nil then
382 begin
383 e_WriteLog(Format('ERROR LOADING MUSIC:', [find_id]), TMsgType.Warning);
384 e_WriteLog(Mix_GetError(), TMsgType.Warning);
385 end
386 else
387 begin
388 dumpMusicType(e_SoundsArray[find_id].Music);
389 end;
390 //SDL_FreeRW(rw);
392 if e_SoundsArray[find_id].Music <> nil then
393 begin
394 Mix_FreeMusic(e_SoundsArray[find_id].Music);
395 end;
396 e_SoundsArray[find_id].Music := nil;
397 Exit;
399 end
400 else
401 begin
402 e_SoundsArray[find_id].Sound := Mix_LoadWAV_RW(rw, 0);
403 end;
404 //SDL_FreeRW(rw); // somehow it segfaults...
405 if (e_SoundsArray[find_id].Sound = nil) and (e_SoundsArray[find_id].Music = nil) then
406 begin
407 e_SoundsArray[find_id].Data := nil;
408 Exit;
409 end;
411 ID := find_id;
413 Result := True;
414 end;
416 function e_PlaySound (ID: DWORD): Integer;
417 var
418 res: Integer = -1;
419 begin
420 Result := -1;
421 if not SoundInitialized then Exit;
423 if e_isSound(ID) then
424 begin
425 if e_SoundsArray[ID].nRefs >= gMaxSimSounds then Exit;
426 Inc(e_SoundsArray[ID].nRefs);
427 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
428 if res >= 0 then
429 begin
430 ChanSIds[res].id := ID;
431 ChanSIds[res].muted := SoundMuted;
432 if SoundMuted then Mix_Volume(res, 0) else Mix_Volume(res, ChanSIds[res].oldvol);
434 if e_SoundsArray[ID].isMusic then
435 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, -1)
436 else
437 res := Mix_PlayChannel(-1, e_SoundsArray[ID].Sound, 0);
439 end;
440 end
441 else
442 begin
443 if not e_isMusic(ID) then Exit;
444 Mix_HaltMusic();
445 res := Mix_PlayMusic(e_SoundsArray[ID].Music, -1);
446 if res >= 0 then res := N_MUSCHAN;
447 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
448 Result := res;
449 end;
451 Result := res;
452 end;
454 function e_chanSetPan (chan: Integer; Pan: Single): Boolean;
455 var
456 l, r: UInt8;
457 begin
458 Result := True;
459 if chan = N_MUSCHAN then
460 begin
461 // no panning for music
462 end
463 else if chan >= 0 then
464 begin
465 if Pan < -1.0 then Pan := -1.0 else if Pan > 1.0 then Pan := 1.0;
466 Pan := Pan+1.0; // 0..2
467 l := trunc(127.0*(2.0-Pan));
468 r := trunc(127.0*Pan);
469 Mix_SetPanning(chan, l, r);
470 ChanSIds[chan].pan := Pan;
471 end
472 else
473 begin
474 Result := False;
475 end;
476 end;
478 function e_chanSetVol (chan: Integer; Volume: Single): Boolean;
479 var
480 vol: Integer;
481 begin
482 Result := True;
483 if Volume < 0 then Volume := 0 else if Volume > 1 then Volume := 1;
484 vol := trunc(Volume*MIX_MAX_VOLUME);
485 if chan = N_MUSCHAN then
486 begin
487 MusVolume := vol;
488 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(vol);
489 end
490 else if chan >= 0 then
491 begin
492 ChanSIds[chan].oldvol := vol;
493 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, vol);
494 end
495 else
496 begin
497 Result := False;
498 end;
499 end;
501 function e_PlaySoundPan(ID: DWORD; Pan: Single): Integer;
502 var
503 chan: Integer;
504 begin
505 Result := -1;
506 chan := e_PlaySound(ID);
507 e_chanSetPan(chan, Pan);
508 Result := chan;
509 end;
511 function e_PlaySoundVolume(ID: DWORD; Volume: Single): Integer;
512 var
513 chan: Integer;
514 begin
515 Result := -1;
516 chan := e_PlaySound(ID);
517 e_chanSetVol(chan, Volume);
518 Result := chan;
519 end;
521 function e_PlaySoundPanVolume(ID: DWORD; Pan, Volume: Single): Integer;
522 var
523 chan: Integer;
524 begin
525 Result := -1;
526 chan := e_PlaySound(ID);
527 e_chanSetPan(chan, Pan);
528 e_chanSetVol(chan, Volume);
529 Result := chan;
530 end;
532 procedure e_DeleteSound(ID: DWORD);
533 var
534 i: Integer;
535 begin
536 if ID > High(e_SoundsArray) then Exit;
537 if (e_SoundsArray[ID].Sound = nil) and (e_SoundsArray[ID].Music = nil) then Exit;
539 for i := 0 to N_CHANNELS-1 do
540 begin
541 if ChanSIds[i].id = ID then
542 begin
543 ChanSIds[i].id := NO_SOUND_ID;
544 Mix_HaltChannel(i);
545 end;
546 end;
548 if e_SoundsArray[ID].Sound <> nil then Mix_FreeChunk(e_SoundsArray[ID].Sound);
549 if e_SoundsArray[ID].Music <> nil then Mix_FreeMusic(e_SoundsArray[ID].Music);
550 if e_SoundsArray[ID].Data <> nil then FreeMem(e_SoundsArray[ID].Data);
552 e_SoundsArray[ID].Sound := nil;
553 e_SoundsArray[ID].Music := nil;
554 e_SoundsArray[ID].Data := nil;
555 e_SoundsArray[ID].nRefs := 0;
556 end;
558 procedure e_ModifyChannelsVolumes(SoundMod: Single; setMode: Boolean);
559 var
560 i: Integer;
561 vol: Single;
562 ovol: Integer;
563 begin
564 for i := 0 to N_CHANNELS-1 do
565 begin
566 ovol := ChanSIds[i].oldvol;
567 if setMode then
568 begin
569 vol := SoundMod;
570 end
571 else
572 begin
573 vol := (MIX_MAX_VOLUME+0.0)/ovol;
574 vol := vol*SoundMod;
575 end;
576 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
577 ChanSIds[i].oldvol := trunc(vol*MIX_MAX_VOLUME);
578 //if i = 0 then e_WriteLog(Format('modifying volumes: vol=%f; newvol=%d', [vol, ChanSIds[i].oldvol]), TMsgType.Warning);
579 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
580 end;
581 ovol := Mix_VolumeMusic(-1);
582 if ovol >= 0 then
583 begin
584 if setMode then
585 begin
586 vol := SoundMod;
587 end
588 else
589 begin
590 vol := (MIX_MAX_VOLUME+0.0)/ovol;
591 vol := vol * SoundMod;
592 end;
593 if vol < 0 then vol := 0 else if vol > 1 then vol := 1;
594 MusVolume := trunc(vol*MIX_MAX_VOLUME);
595 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
596 end;
597 end;
599 procedure e_MuteChannels(Enable: Boolean);
600 var
601 i: Integer;
602 begin
603 //if Enable = SoundMuted then Exit;
604 SoundMuted := Enable;
605 for i := 0 to N_CHANNELS-1 do
606 begin
607 if ChanSIds[i].muted <> SoundMuted then
608 begin
609 ChanSIds[i].muted := SoundMuted;
610 //e_WriteLog(Format('gmuting sound for channel %d', [i]), TMsgType.Warning);
611 if ChanSIds[i].muted then Mix_Volume(i, 0) else Mix_Volume(i, ChanSIds[i].oldvol);
612 end;
613 end;
614 //if SoundMuted then e_WriteLog('muting music', TMsgType.Notify) else e_WriteLog(Format('unmuting music (%d)', [MusVolume]), TMsgType.Notify);
615 if SoundMuted then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
616 end;
618 procedure e_StopChannels();
619 var
620 i: Integer;
621 begin
622 Mix_HaltChannel(-1);
623 Mix_HaltMusic();
624 for i := 0 to High(e_SoundsArray) do e_SoundsArray[i].nRefs := 0;
625 for i := 0 to N_CHANNELS-1 do ChanSIds[i].id := NO_SOUND_ID;
626 end;
628 procedure e_RemoveAllSounds();
629 var
630 i: Integer;
631 begin
632 if SoundInitialized then e_StopChannels();
633 for i := 0 to High(e_SoundsArray) do e_DeleteSound(i);
634 SetLength(e_SoundsArray, 0);
635 e_SoundsArray := nil;
636 end;
638 procedure e_ReleaseSoundSystem();
639 begin
640 e_RemoveAllSounds();
641 if SoundInitialized then
642 begin
643 Mix_CloseAudio();
644 SoundInitialized := False;
645 end;
646 end;
648 procedure e_SoundUpdate();
649 begin
650 //FMOD_System_Update(F_System);
651 end;
654 { TBasicSound: }
656 constructor TBasicSound.Create();
657 begin
658 FID := NO_SOUND_ID;
659 FMusic := False;
660 FChanNum := -1;
661 FPosition := 0;
662 FPriority := 128;
663 end;
665 destructor TBasicSound.Destroy();
666 begin
667 FreeSound();
668 inherited;
669 end;
671 function TBasicSound.GetChan (): Integer;
672 begin
673 if (FID <> NO_SOUND_ID) and (FChanNum >= 0) and (FChanNum < N_CHANNELS) then
674 begin
675 if ChanSIds[FChanNum].id <> FID then FChanNum := -1;
676 end
677 else if e_isMusic(FID) then
678 begin
679 FChanNum := N_MUSCHAN;
680 end;
681 Result := FChanNum;
682 end;
684 procedure TBasicSound.FreeSound();
685 begin
686 if FID = NO_SOUND_ID then Exit;
687 Stop();
688 FID := NO_SOUND_ID;
689 FMusic := False;
690 FPosition := 0;
691 FChanNum := -1;
692 end;
694 // aPos: msecs
695 function TBasicSound.RawPlay(Pan: Single; Volume: Single; aPos: DWORD): Boolean;
696 begin
697 Result := False;
698 if (FID = NO_SOUND_ID) or not SoundInitialized then Exit;
699 FChanNum := e_PlaySoundPanVolume(FID, Pan, Volume);
700 Result := (FChanNum >= 0);
701 //if e_isMusic(FID) then e_WriteLog(Format('playing music (%u)', [FID]), TMsgType.Notify);
702 //TODO: aPos
703 end;
705 procedure TBasicSound.SetID(ID: DWORD);
706 begin
707 FreeSound();
708 FID := ID;
709 if ID <> NO_SOUND_ID then
710 begin
711 FMusic := e_SoundsArray[ID].isMusic;
712 end;
713 FChanNum := -1;
714 end;
716 function TBasicSound.IsPlaying(): Boolean;
717 var
718 chan: Integer;
719 begin
720 Result := False;
721 if e_isSound(FID) then
722 begin
723 //e_WriteLog(Format('IsPlaying: FID=%u; FChanNum=%d', [FID, FChanNum]), TMsgType.Warning);
724 chan := Channel;
725 if chan < 0 then
726 begin
727 //e_WriteLog(Format('IsPlaying: FID=%u; ONA', [FID]), TMsgType.Warning);
728 Exit;
729 end;
730 //Result := (Mix_Playing(chan) > 0)
731 //e_WriteLog(Format('IsPlaying: FID=%u; TAN', [FID]), TMsgType.Warning);
732 Result := True;
733 end
734 else if e_isMusic(FID) then
735 begin
736 Result := (Mix_PlayingMusic() > 0);
737 end;
738 end;
740 procedure TBasicSound.Stop();
741 var
742 chan: Integer;
743 begin
744 if e_isSound(FID) then
745 begin
746 chan := Channel;
747 if chan >= 0 then
748 begin
749 //GetPosition();
750 Mix_HaltChannel(chan);
751 end;
752 end
753 else if e_isMusic(FID) then
754 begin
755 Mix_HaltMusic();
756 end;
757 FChanNum := -1;
758 end;
760 function TBasicSound.IsPaused(): Boolean;
761 var
762 chan: Integer;
763 begin
764 Result := False;
765 if e_isSound(FID) then
766 begin
767 chan := Channel;
768 if chan < 0 then Exit;
769 Result := (Mix_Paused(chan) > 0);
770 end
771 else if e_isMusic(FID) then
772 begin
773 Result := (Mix_PausedMusic() > 0);
774 end;
775 end;
777 procedure TBasicSound.Pause(Enable: Boolean);
778 var
779 chan: Integer;
780 pl: Boolean;
781 begin
782 Enable := not Enable; // fuckin' double negation
783 if e_isSound(FID) then
784 begin
785 chan := Channel;
786 if chan < 0 then Exit;
787 pl := not (Mix_Paused(chan) > 0);
788 if pl <> Enable then
789 begin
790 if Enable then Mix_Resume(chan) else Mix_Pause(chan);
791 end;
792 end
793 else if e_isMusic(FID) then
794 begin
795 pl := not (Mix_PausedMusic() > 0);
796 if pl <> Enable then
797 begin
798 if Enable then Mix_ResumeMusic() else Mix_PauseMusic();
799 end;
800 end;
802 if Enable then
803 begin
804 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
805 if res <> FMOD_OK then
806 begin
807 end;
808 end;
810 end;
812 function TBasicSound.GetVolume(): Single;
813 var
814 chan: Integer;
815 begin
816 Result := 0.0;
817 if e_isSound(FID) then
818 begin
819 chan := Channel;
820 if chan < 0 then Exit;
821 Result := (ChanSIds[chan].oldvol+0.0)/(MIX_MAX_VOLUME+0.0);
822 end
823 else if e_isMusic(FID) then
824 begin
825 Result := (MusVolume+0.0)/(MIX_MAX_VOLUME+0.0);
826 end;
827 end;
829 procedure TBasicSound.SetVolume(Volume: Single);
830 var
831 chan: Integer;
832 begin
833 if e_isSound(FID) then
834 begin
835 chan := Channel;
836 if chan < 0 then Exit;
837 //e_WriteLog(Format('SetVolume: chan=%d; Volume=%f', [chan, Volume]), TMsgType.Warning);
838 e_chanSetVol(chan, Volume);
839 end
840 else if e_isMusic(FID) then
841 begin
842 //e_WriteLog(Format('SetVolume: chan=MUSIC; Volume=%f', [Volume]), TMsgType.Warning);
843 e_chanSetVol(N_MUSCHAN, Volume);
844 end;
845 end;
847 function TBasicSound.GetPan(): Single;
848 var
849 chan: Integer;
850 begin
851 Result := 1.0;
852 if e_isSound(FID) then
853 begin
854 chan := Channel;
855 if chan < 0 then Exit;
856 Result := ChanSIds[chan].pan;
857 end;
858 end;
860 procedure TBasicSound.SetPan(Pan: Single);
861 var
862 chan: Integer;
863 begin
864 if e_isSound(FID) then
865 begin
866 chan := Channel;
867 if chan < 0 then Exit;
868 e_chanSetPan(chan, Pan);
869 end;
870 end;
872 function TBasicSound.IsMuted(): Boolean;
873 var
874 chan: Integer;
875 begin
876 Result := False;
877 if e_isSound(FID) then
878 begin
879 chan := Channel;
880 if chan < 0 then Exit;
881 Result := ChanSIds[chan].muted;
882 end
883 else if e_isMusic(FID) then
884 begin
885 Result := SoundMuted;
886 end;
887 end;
889 procedure TBasicSound.Mute(Enable: Boolean);
890 var
891 chan: Integer;
892 begin
893 if e_isSound(FID) then
894 begin
895 chan := Channel;
896 if chan < 0 then Exit;
897 if ChanSIds[chan].muted <> Enable then
898 begin
899 //e_WriteLog(Format('muting sound for channel %d', [cnan]), TMsgType.Warning);
900 ChanSIds[chan].muted := Enable;
901 if ChanSIds[chan].muted then Mix_Volume(chan, 0) else Mix_Volume(chan, ChanSIds[chan].oldvol);
902 end;
903 end
904 else if e_isMusic(FID) then
905 begin
906 if Enable then Mix_VolumeMusic(0) else Mix_VolumeMusic(MusVolume);
907 end;
908 end;
910 //TODO
911 function TBasicSound.GetPosition(): DWORD;
912 begin
913 Result := 0;
915 if FChanNum < 0 then Exit;
916 res := FMOD_Channel_GetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
917 if res <> FMOD_OK then
918 begin
919 Exit;
920 end;
921 Result := FPosition;
923 end;
925 //TODO
926 procedure TBasicSound.SetPosition(aPos: DWORD);
927 begin
928 FPosition := aPos;
930 if FChanNum < 0 then Exit;
931 res := FMOD_Channel_SetPosition(FChanNum, FPosition, FMOD_TIMEUNIT_MS);
932 if res <> FMOD_OK then
933 begin
934 end;
936 end;
938 //TODO
939 procedure TBasicSound.SetPriority(priority: Integer);
940 begin
942 if (FChanNum <> nil) and (FPriority <> priority) and
943 (priority >= 0) and (priority <= 256) then
944 begin
945 FPriority := priority;
946 res := FMOD_Channel_SetPriority(FChanNum, priority);
947 if res <> FMOD_OK then
948 begin
949 end;
950 end;
952 end;
954 end.