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