1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name fluidsynth.cpp - FluidSynth support */
12 //
13 // (c) Copyright 2014 by cybermind
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 /*----------------------------------------------------------------------------
33 -- Includes
34 ----------------------------------------------------------------------------*/
35
36 #include "stratagus.h"
37
38 #ifdef USE_FLUIDSYNTH // {
39
40 #include "sound.h"
41 #include "sound_server.h"
42 #include "iolib.h"
43 #include "unit/unit.h"
44
45 #include <fluidsynth.h>
46
47 /*----------------------------------------------------------------------------
48 -- Declarations
49 ----------------------------------------------------------------------------*/
50
51 struct FluidSynthData {
52 CFile *MIDIFile; /// MIDI file handle
53 };
54
55 class CSynthesizer {
56 public:
CSynthesizer()57 CSynthesizer() : Settings(nullptr), Synth(nullptr), Player(nullptr), State(StateCleaned) {}
58
59 fluid_settings_t *Settings;
60 fluid_synth_t *Synth;
61 fluid_player_t *Player;
62
63 SynthState State;
64 };
65
66 CSynthesizer FluidSynthesizer;
67
68 class CSampleFluidSynth : public CSample
69 {
70 public:
71 ~CSampleFluidSynth();
72 int Read(void *buf, int len);
73
74 FluidSynthData Data;
75 };
76
77 class CSampleFluidSynthStream : public CSample
78 {
79 public:
80 ~CSampleFluidSynthStream();
81 int Read(void *buf, int len);
82
83 FluidSynthData Data;
84 };
85
86 /*----------------------------------------------------------------------------
87 -- Functions
88 ----------------------------------------------------------------------------*/
89
90 /**
91 ** Type member function to read from the MIDI file
92 **
93 ** @param buf Buffer to write data to
94 ** @param len Length of the buffer
95 **
96 ** @return Number of bytes read
97 */
Read(void * buf,int len)98 int CSampleFluidSynthStream::Read(void *buf, int len)
99 {
100 while (this->Len < SOUND_BUFFER_SIZE / 2 && fluid_player_get_status(FluidSynthesizer.Player) == FLUID_PLAYER_PLAYING) {
101 memcpy(this->Buffer, this->Buffer + this->Pos, this->Len);
102 this->Pos = 0;
103 fluid_synth_write_s16(FluidSynthesizer.Synth, SOUND_BUFFER_SIZE / 8, this->Buffer + this->Len, 0, 2,
104 this->Buffer + this->Len, 1, 2);
105 this->Len += SOUND_BUFFER_SIZE / 2;
106 }
107
108 if (this->Len < len) {
109 // EOF
110 len = this->Len;
111 }
112
113 memcpy(buf, this->Buffer + this->Pos, len);
114 this->Len -= len;
115 this->Pos += len;
116
117 return len;
118 }
119
120 /**
121 ** Type member function to free sample
122 */
~CSampleFluidSynthStream()123 CSampleFluidSynthStream::~CSampleFluidSynthStream()
124 {
125 this->Data.MIDIFile->close();
126 delete this->Data.MIDIFile;
127 delete[] this->Buffer;
128 }
129
130 /**
131 ** Type member function to read from the module
132 **
133 ** @param buf Buffer to write data to
134 ** @param len Length of the buffer
135 **
136 ** @return Number of bytes read
137 */
Read(void * buf,int len)138 int CSampleFluidSynth::Read(void *buf, int len)
139 {
140 /* TO-DO: not supported yet*/
141
142 return len;
143 }
144
145 /**
146 ** Type member function to free sample
147 */
~CSampleFluidSynth()148 CSampleFluidSynth::~CSampleFluidSynth()
149 {
150 delete[] this->Buffer;
151 }
152
153 /**
154 ** Gets the state of Fluidsynth player
155 **
156 */
GetFluidSynthState()157 SynthState GetFluidSynthState()
158 {
159 return FluidSynthesizer.State;
160 }
161
162 /**
163 ** Cleans FluidSynth data
164 **
165 */
CleanFluidSynth(bool reinit)166 void CleanFluidSynth(bool reinit)
167 {
168 if (reinit) {
169 delete_fluid_player(FluidSynthesizer.Player);
170 FluidSynthesizer.Player = new_fluid_player(FluidSynthesizer.Synth);
171 FluidSynthesizer.State = StateInitialized;
172 } else if (FluidSynthesizer.State != StateCleaned) {
173 if (FluidSynthesizer.Player) {
174 delete_fluid_player(FluidSynthesizer.Player);
175 }
176 if (FluidSynthesizer.Synth) {
177 delete_fluid_synth(FluidSynthesizer.Synth);
178 }
179 if (FluidSynthesizer.Settings) {
180 delete_fluid_settings(FluidSynthesizer.Settings);
181 }
182 FluidSynthesizer.State = StateCleaned;
183 }
184 }
185
186 /**
187 ** Inits FluidSynth and loads SF2 soundfont
188 **
189 */
InitFluidSynth()190 int InitFluidSynth()
191 {
192 // Don't reinit
193 if (FluidSynthesizer.State > StateCleaned) {
194 return 0;
195 }
196 FluidSynthesizer.State = StateInitialized;
197 // Settings
198 FluidSynthesizer.Settings = new_fluid_settings();
199 if (FluidSynthesizer.Settings == nullptr) {
200 fprintf(stderr, "Failed to create the FluidSynth settings\n");
201 CleanFluidSynth();
202 return -1;
203 }
204 // Default settings
205 fluid_settings_setstr(FluidSynthesizer.Settings, "audio.file.type", "raw");
206 fluid_settings_setnum(FluidSynthesizer.Settings, "synth.sample-rate", 44100);
207 fluid_settings_setstr(FluidSynthesizer.Settings, "audio.file.format", "s16");
208 fluid_settings_setstr(FluidSynthesizer.Settings, "audio.file.endian", "little");
209 fluid_settings_setint(FluidSynthesizer.Settings, "audio.period-size", 4096);
210 fluid_settings_setint(FluidSynthesizer.Settings, "synth.parallel-render", 1);
211 fluid_settings_setnum(FluidSynthesizer.Settings, "synth.gain", 0.2);
212 fluid_settings_setstr(FluidSynthesizer.Settings, "synth-midi-bank-select", "gm");
213 fluid_settings_setint(FluidSynthesizer.Settings, "synth.synth-polyphony", 24);
214
215 // Synthesizer itself
216 FluidSynthesizer.Synth = new_fluid_synth(FluidSynthesizer.Settings);
217 if (FluidSynthesizer.Synth == nullptr) {
218 fprintf(stderr, "Failed to create the SF2 synthesizer\n");
219 CleanFluidSynth();
220 return -1;
221 }
222 // Load the soundfont
223 std::string sf2Path = LibraryFileName(Preference.SF2Soundfont.c_str());
224 if (fluid_synth_sfload(FluidSynthesizer.Synth, sf2Path.c_str(), 1) == -1) {
225 if (fluid_synth_sfload(FluidSynthesizer.Synth, Preference.SF2Soundfont.c_str(), 1) == -1) {
226 fprintf(stderr, "Failed to load the SoundFont: %s\n", Preference.SF2Soundfont.c_str());
227 CleanFluidSynth();
228 return -1;
229 }
230 }
231 // Create player
232 FluidSynthesizer.Player = new_fluid_player(FluidSynthesizer.Synth);
233 if (FluidSynthesizer.Player == nullptr) {
234 fprintf(stderr, "Failed to create SF2 player\n");
235 CleanFluidSynth();
236 return -1;
237 }
238 return 0;
239 }
240
241 /**
242 ** Load MIDI file using FluidSynth library.
243 **
244 ** @param name MIDI file.
245 ** @param flags Unused.
246 **
247 ** @return Returns the loaded sample.
248 */
LoadFluidSynth(const char * name,int flags)249 CSample *LoadFluidSynth(const char *name, int flags)
250 {
251 CSample *sample;
252 FluidSynthData *data;
253 CFile *f;
254 char s[256];
255
256 // If library isn't loaded, load it now
257 if (FluidSynthesizer.State == StateCleaned) {
258 if (InitFluidSynth() != 0) {
259 DebugPrint("Can't init FluidSynth!\n");
260 return nullptr;
261 }
262 } else if (FluidSynthesizer.State == StatePlaying) {
263 // Reinit the player
264 CleanFluidSynth(true);
265 }
266
267 strcpy_s(s, sizeof(s), name);
268 f = new CFile;
269 if (f->open(name, CL_OPEN_READ) == -1) {
270 delete f;
271 return nullptr;
272 }
273
274 // check if this is a MIDI file
275 if (!fluid_is_midifile(name)) {
276 fprintf(stderr, "Not a MIDI file: %s\n", name);
277 f->close();
278 delete f;
279 return nullptr;
280 } else {
281 fluid_player_add(FluidSynthesizer.Player, name);
282 }
283
284 if (flags & PlayAudioStream) {
285 CSampleFluidSynthStream *sampleFluidSynthStream = new CSampleFluidSynthStream;
286 sample = sampleFluidSynthStream;
287 data = &sampleFluidSynthStream->Data;
288 } else {
289 CSampleFluidSynth *sampleFluidSynth = new CSampleFluidSynth;
290 sample = sampleFluidSynth;
291 data = &sampleFluidSynth->Data;
292 }
293 data->MIDIFile = f;
294 sample->Channels = 2;
295 sample->SampleSize = 16;
296 sample->Frequency = 44100;
297 sample->Pos = 0;
298 //Wyrmgus start
299 sample->File = name;
300 //Wyrmgus end
301
302 if (flags & PlayAudioStream) {
303 sample->Len = 0;
304 sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
305
306 fluid_player_play(FluidSynthesizer.Player);
307 FluidSynthesizer.State = StatePlaying;
308 } else {
309 /* not supported yet*/
310 f->close();
311 delete f;
312 return nullptr;
313 }
314
315 return sample;
316 }
317
318 #endif // } USE_FLUIDSYNTH
319
320 //@}
321