1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
16 unit e_soundfile_fluid
;
20 uses e_soundfile
, fluidsynth
;
23 // a midi loader that uses fluidsynth
25 TFluidLoader
= class (TSoundLoader
)
27 function Load(Data
: Pointer; Len
: LongWord; Loop
: Boolean): Boolean; override; overload
;
28 function Load(FName
: string; Loop
: Boolean): Boolean; override; overload
;
29 function Finished(): Boolean; override;
30 function Restart(): Boolean; override;
31 function FillBuffer(Buf
: Pointer; Len
: LongWord): LongWord; override;
32 procedure Free(); override;
35 FSynth
: pfluid_synth_t
;
36 FPlayer
: pfluid_player_t
;
39 TFluidLoaderFactory
= class (TSoundLoaderFactory
)
41 function MatchHeader(Data
: Pointer; Len
: LongWord): Boolean; override;
42 function MatchExtension(FName
: string): Boolean; override;
43 function GetLoader(): TSoundLoader
; override;
47 DEFAULT_SOUNDFONT
= 'data/soundfont.sf2';
51 uses sysutils
, utils
, e_sound
, e_log
, ctypes
{$IFDEF WINDOWS}, windirs
{$ENDIF};
54 FluidSettings
: pfluid_settings_t
= nil;
56 function FindDefaultSoundfont(): string;
59 SfNames
: array [0..1] of string = (
60 // creative soundfonts
68 SysDir
:= GetWindowsSpecialDir(CSIDL_SYSTEM
, False);
69 for I
:= Low(SfNames
) to High(SfNames
) do
71 S
:= SysDir
+ SfNames
[I
];
74 e_LogWriteln('FluidSynth: Found system soundfont ' + S
);
79 Result
:= DEFAULT_SOUNDFONT
;
83 Result
:= DEFAULT_SOUNDFONT
;
87 (* TFluidLoaderFactory *)
89 function TFluidLoaderFactory
.MatchHeader(Data
: Pointer; Len
: LongWord): Boolean;
93 MIDIHDR
= $6468544D; // 'MThd'
96 if Len
< 14 then Exit
; // the header is at least 4+4+6 bytes
98 Result
:= ((P
+0)^ = MIDIHDR
) and ((P
+1)^ <> 0); // header length is not 0
101 function TFluidLoaderFactory
.MatchExtension(FName
: string): Boolean;
105 Ext
:= GetFilenameExt(FName
);
106 Result
:= (Ext
= '.mid') or (Ext
= '.midi');
109 function TFluidLoaderFactory
.GetLoader(): TSoundLoader
;
111 if e_SoundFont
= '' then e_SoundFont
:= FindDefaultSoundfont();
112 Result
:= TFluidLoader
.Create();
117 function TFluidLoader
.Load(Data
: Pointer; Len
: LongWord; Loop
: Boolean): Boolean;
124 FSynth
:= new_fluid_synth(FluidSettings
);
126 raise Exception
.Create('new_fluid_synth failed');
127 Ret
:= fluid_synth_sfload(FSynth
, PChar(e_SoundFont
), 1);
128 if Ret
= FLUID_FAILED
then
129 raise Exception
.Create('fluid_synth_sfload failed');
130 FPlayer
:= new_fluid_player(FSynth
);
131 if FPlayer
= nil then
132 raise Exception
.Create('new_fluid_player failed');
133 Ret
:= fluid_player_add_mem(FPlayer
, Data
, Len
);
134 if Ret
= FLUID_FAILED
then
135 raise Exception
.Create('fluid_player_add failed');
136 fluid_player_play(FPlayer
);
140 e_LogWriteln('FluidSynth: Load(Data) failed: ' + E
.Message);
141 if FPlayer
<> nil then delete_fluid_player(FPlayer
);
142 if FSynth
<> nil then delete_fluid_synth(FSynth
);
150 fluid_player_set_loop(FPlayer
, -1);
152 FFormat
.SampleRate
:= 44100;
153 FFormat
.SampleBits
:= 16;
154 FFormat
.Channels
:= 2;
160 function TFluidLoader
.Load(FName
: string; Loop
: Boolean): Boolean;
167 FSynth
:= new_fluid_synth(FluidSettings
);
169 raise Exception
.Create('new_fluid_synth failed');
170 Ret
:= fluid_synth_sfload(FSynth
, PChar(e_SoundFont
), 1);
171 if Ret
= FLUID_FAILED
then
172 raise Exception
.Create('fluid_synth_sfload failed');
173 FPlayer
:= new_fluid_player(FSynth
);
174 if FPlayer
= nil then
175 raise Exception
.Create('new_fluid_player failed');
176 Ret
:= fluid_player_add(FPlayer
, PChar(FName
));
177 if Ret
= FLUID_FAILED
then
178 raise Exception
.Create('fluid_player_add failed');
179 fluid_player_play(FPlayer
);
183 e_LogWriteln('FluidSynth: Load(Data) failed: ' + E
.Message);
184 if FPlayer
<> nil then delete_fluid_player(FPlayer
);
185 if FSynth
<> nil then delete_fluid_synth(FSynth
);
193 fluid_player_set_loop(FPlayer
, -1);
195 FFormat
.SampleRate
:= 44100;
196 FFormat
.SampleBits
:= 16;
197 FFormat
.Channels
:= 2;
203 function TFluidLoader
.Finished(): Boolean;
205 Result
:= fluid_player_get_status(FPlayer
) = FLUID_PLAYER_DONE
;
208 function TFluidLoader
.Restart(): Boolean;
211 // fluid_player_seek() is only supported in full 2.x.x, and I ain't compiling that shit
212 // if (FSynth <> nil) and (FPlayer <> nil) then
214 // fluid_synth_system_reset(FSynth);
215 // fluid_player_seek(FPlayer, 0);
216 // fluid_player_play(FPlayer);
221 function TFluidLoader
.FillBuffer(Buf
: Pointer; Len
: LongWord): LongWord;
226 if (FSynth
= nil) or (FPlayer
= nil) then Exit
;
227 Ret
:= fluid_synth_write_s16(FSynth
, Len
div 4, Buf
, 0, 2, Buf
, 1, 2);
228 if Ret
= FLUID_OK
then Result
:= Len
;
231 procedure TFluidLoader
.Free();
233 if FPlayer
<> nil then
235 fluid_player_stop(FPlayer
);
236 delete_fluid_player(FPlayer
);
238 if FSynth
<> nil then delete_fluid_synth(FSynth
);
244 FluidSettings
:= new_fluid_settings();
245 if FluidSettings
<> nil then
247 fluid_settings_setint(FluidSettings
, PChar('synth.midi-channels'), 16);
248 fluid_settings_setint(FluidSettings
, PChar('synth.cpu-cores'), 1);
249 fluid_settings_setnum(FluidSettings
, PChar('synth.sample-rate'), 44100);
250 fluid_settings_setnum(FluidSettings
, PChar('synth.gain'), 1);
251 fluid_settings_setint(FluidSettings
, PChar('synth.reverb.active'), 0);
252 fluid_settings_setint(FluidSettings
, PChar('synth.chorus.active'), 0);
253 fluid_settings_setstr(FluidSettings
, PChar('player.timing-source'), PChar('sample'));
254 e_AddSoundLoader(TFluidLoaderFactory
.Create());
257 if FluidSettings
<> nil then
258 delete_fluid_settings(FluidSettings
);