DEADSOFTWARE

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