Move decoder (de)initialization into their respective classes
[alure.git] / src / codec_fluidsynth.cpp
blobd1d654758e19facab8e1ce9e7d4a7a5c36d646cb
1 /*
2 * ALURE OpenAL utility library
3 * Copyright (c) 2009-2010 by Chris Robinson.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
24 #include "config.h"
26 #include "main.h"
28 #include <string.h>
29 #include <assert.h>
31 #include <istream>
33 #include <fluidsynth.h>
36 #ifdef _WIN32
37 #define FLUIDSYNTH_LIB "libfluidsynth.dll"
38 #elif defined(__APPLE__)
39 #define FLUIDSYNTH_LIB "libfluidsynth.1.dylib"
40 #else
41 #define FLUIDSYNTH_LIB "libfluidsynth.so.1"
42 #endif
44 static void *fsynth_handle;
45 #define MAKE_FUNC(x) static typeof(x)* p##x
46 MAKE_FUNC(fluid_settings_setstr);
47 MAKE_FUNC(fluid_synth_program_change);
48 MAKE_FUNC(fluid_synth_sfload);
49 MAKE_FUNC(fluid_settings_setnum);
50 MAKE_FUNC(fluid_synth_sysex);
51 MAKE_FUNC(fluid_synth_cc);
52 MAKE_FUNC(fluid_synth_pitch_bend);
53 MAKE_FUNC(fluid_synth_channel_pressure);
54 MAKE_FUNC(fluid_synth_write_float);
55 MAKE_FUNC(new_fluid_synth);
56 MAKE_FUNC(delete_fluid_settings);
57 MAKE_FUNC(delete_fluid_synth);
58 MAKE_FUNC(fluid_synth_program_reset);
59 MAKE_FUNC(fluid_settings_setint);
60 MAKE_FUNC(new_fluid_settings);
61 MAKE_FUNC(fluid_synth_write_s16);
62 MAKE_FUNC(fluid_synth_noteoff);
63 MAKE_FUNC(fluid_synth_sfunload);
64 MAKE_FUNC(fluid_synth_noteon);
65 #undef MAKE_FUNC
68 struct fluidStream : public alureStream {
69 private:
70 static const ALubyte MIDI_CHANNEL_MASK = 0x0F;
71 static const ALubyte MIDI_EVENT_MASK = 0xF0;
73 static const ALubyte MIDI_NOTEOFF = 0x80; // + note + velocity
74 static const ALubyte MIDI_NOTEON = 0x90; // + note + velocity
75 static const ALubyte MIDI_POLYPRESS = 0xA0; // + pressure (2 bytes)
76 static const ALubyte MIDI_CTRLCHANGE = 0xB0; // + ctrl + value
77 static const ALubyte MIDI_PRGMCHANGE = 0xC0; // + new patch
78 static const ALubyte MIDI_CHANPRESS = 0xD0; // + pressure (1 byte)
79 static const ALubyte MIDI_PITCHBEND = 0xE0; // + pitch bend (2 bytes)
80 static const ALubyte MIDI_SPECIAL = 0xF0; // Special event
82 static const ALubyte MIDI_SYSEX = 0xF0; // SysEx begin
83 static const ALubyte MIDI_SYSEXEND = 0xF7; // SysEx end
84 static const ALubyte MIDI_SONGPOS = 0xF2; // Song position
85 static const ALubyte MIDI_SONGSEL = 0xF3; // Song select
86 static const ALubyte MIDI_META = 0xFF; // Meta event begin
88 static const ALubyte MIDI_META_EOT = 0x2F; // End-of-track
89 static const ALubyte MIDI_META_TEMPO = 0x51; // Tempo change
91 struct MidiTrack {
92 std::vector<ALubyte> data;
93 size_t Offset;
94 ALubyte LastEvent;
95 ALdouble SamplesLeft;
97 MidiTrack() : Offset(0), LastEvent(0), SamplesLeft(0.)
98 { }
99 MidiTrack(const MidiTrack &rhs)
100 : data(rhs.data), Offset(rhs.Offset), LastEvent(rhs.LastEvent),
101 SamplesLeft(rhs.SamplesLeft)
104 void Reset()
106 Offset = 0;
107 LastEvent = 0;
108 SamplesLeft = 0.;
111 const MidiTrack& operator=(const MidiTrack &rhs)
113 data = rhs.data;
114 Offset = rhs.Offset;
115 LastEvent = rhs.LastEvent;
116 SamplesLeft = rhs.SamplesLeft;
117 return *this;
120 unsigned long ReadVarLen()
122 if(Offset >= data.size())
123 return 0;
125 unsigned long len = data[Offset]&0x7F;
126 while((data[Offset]&0x80))
128 if(++Offset >= data.size())
129 return 0;
130 len = (len<<7) | (data[Offset]&0x7F);
132 Offset++;
134 return len;
138 ALuint Divisions;
139 std::vector<MidiTrack> Tracks;
141 ALenum format;
142 ALsizei sampleRate;
143 ALdouble samplesPerTick;
145 fluid_settings_t *fluidSettings;
146 fluid_synth_t *fluidSynth;
147 int fontID;
149 public:
150 static void Init()
152 fsynth_handle = OpenLib(FLUIDSYNTH_LIB);
153 if(!fsynth_handle)
155 LOAD_FUNC(fsynth_handle, fluid_settings_setstr);
156 LOAD_FUNC(fsynth_handle, fluid_synth_program_change);
157 LOAD_FUNC(fsynth_handle, fluid_synth_sfload);
158 LOAD_FUNC(fsynth_handle, fluid_settings_setnum);
159 LOAD_FUNC(fsynth_handle, fluid_synth_sysex);
160 LOAD_FUNC(fsynth_handle, fluid_synth_cc);
161 LOAD_FUNC(fsynth_handle, fluid_synth_pitch_bend);
162 LOAD_FUNC(fsynth_handle, fluid_synth_channel_pressure);
163 LOAD_FUNC(fsynth_handle, fluid_synth_write_float);
164 LOAD_FUNC(fsynth_handle, new_fluid_synth);
165 LOAD_FUNC(fsynth_handle, delete_fluid_settings);
166 LOAD_FUNC(fsynth_handle, delete_fluid_synth);
167 LOAD_FUNC(fsynth_handle, fluid_synth_program_reset);
168 LOAD_FUNC(fsynth_handle, fluid_settings_setint);
169 LOAD_FUNC(fsynth_handle, new_fluid_settings);
170 LOAD_FUNC(fsynth_handle, fluid_synth_write_s16);
171 LOAD_FUNC(fsynth_handle, fluid_synth_noteoff);
172 LOAD_FUNC(fsynth_handle, fluid_synth_sfunload);
173 LOAD_FUNC(fsynth_handle, fluid_synth_noteon);
175 static void Deinit()
177 if(fsynth_handle)
178 CloseLib(fsynth_handle);
179 fsynth_handle = NULL;
182 virtual bool IsValid()
183 { return fluidSynth != NULL; }
185 virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
187 if(format == AL_NONE)
189 format = GetSampleFormat(2, 32, true);
190 if(format == AL_NONE)
191 format = AL_FORMAT_STEREO16;
193 *fmt = format;
194 *frequency = sampleRate;
195 *blockalign = 2 * ((format==AL_FORMAT_STEREO16) ? sizeof(ALshort) :
196 sizeof(ALfloat));
197 return true;
200 virtual ALuint GetData(ALubyte *data, ALuint bytes)
202 ALuint ret;
204 if(format == AL_FORMAT_STEREO16)
206 ALshort *ptr = reinterpret_cast<ALshort*>(data);
207 ret = FillBuffer(ptr, bytes/2/sizeof(ALshort));
208 ret *= 2 * sizeof(ALshort);
210 else
212 ALfloat *ptr = reinterpret_cast<ALfloat*>(data);
213 ret = FillBuffer(ptr, bytes/2/sizeof(ALfloat));
214 ret *= 2 * sizeof(ALfloat);
217 return ret;
220 virtual bool Rewind()
222 for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
224 i->Reset();
225 unsigned long val = i->ReadVarLen();
226 i->SamplesLeft += val * samplesPerTick;
228 pfluid_synth_program_reset(fluidSynth);
229 UpdateTempo(500000);
230 return true;
233 virtual bool SetPatchset(const char *sfont)
235 int newid = pfluid_synth_sfload(fluidSynth, sfont, true);
236 if(newid == FLUID_FAILED)
238 SetError("Failed to load soundfont");
239 return false;
242 if(fontID != FLUID_FAILED)
243 pfluid_synth_sfunload(fluidSynth, fontID, true);
244 fontID = newid;
246 return true;
249 fluidStream(std::istream *_fstream)
250 : alureStream(_fstream), Divisions(100),
251 format(AL_NONE), sampleRate(48000), samplesPerTick(1.),
252 fluidSettings(NULL), fluidSynth(NULL), fontID(FLUID_FAILED)
254 if(!fsynth_handle) return;
256 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
257 if(device)
258 alcGetIntegerv(device, ALC_FREQUENCY, 1, &sampleRate);
260 char hdr[4];
261 if(!fstream->read(hdr, 4))
262 return;
264 if(memcmp(hdr, "MThd", 4) == 0)
266 ALuint len = read_be32(fstream);
267 if(len != 6)
268 return;
270 int type = read_be16(fstream);
271 if(type != 0 && type != 1)
272 return;
274 ALuint numtracks = read_be16(fstream);
276 Divisions = read_be16(fstream);
277 UpdateTempo(500000);
279 Tracks.resize(numtracks);
280 for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
282 if(!fstream->read(hdr, 4) || memcmp(hdr, "MTrk", 4) != 0)
283 return;
285 ALuint len = read_be32(fstream);
286 i->data.resize(len);
287 if(!fstream->read(reinterpret_cast<char*>(&i->data[0]), len))
288 return;
290 unsigned long val = i->ReadVarLen();
291 i->SamplesLeft += val * samplesPerTick;
293 SetupSynth();
297 virtual ~fluidStream()
299 if(fontID != FLUID_FAILED)
300 pfluid_synth_sfunload(fluidSynth, fontID, true);
301 fontID = FLUID_FAILED;
303 if(fluidSynth != NULL)
304 pdelete_fluid_synth(fluidSynth);
305 fluidSynth = NULL;
307 if(fluidSettings != NULL)
308 pdelete_fluid_settings(fluidSettings);
309 fluidSettings = NULL;
312 private:
313 template<typename T>
314 ALuint FillBuffer(T *Buffer, ALuint BufferSamples)
316 ALuint SamplesInBuffer = 0;
317 while(SamplesInBuffer < BufferSamples)
319 // Check if any tracks are still playing and how many samples are waiting to render
320 size_t TracksPlaying = 0;
321 ALuint SamplesToDo = BufferSamples - SamplesInBuffer;
322 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
323 end = Tracks.end();i != end;i++)
325 if(i->Offset < i->data.size())
327 SamplesToDo = std::min<ALuint>(SamplesToDo, i->SamplesLeft);
328 TracksPlaying++;
331 if(TracksPlaying == 0)
332 break;
334 if(SamplesToDo == 0)
336 ProcessMidi();
337 continue;
340 // Render samples
341 WriteSamples(SamplesToDo, Buffer);
342 Buffer += SamplesToDo*2;
343 SamplesInBuffer += SamplesToDo;
345 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
346 end = Tracks.end();i != end;i++)
348 if(i->Offset < i->data.size())
349 i->SamplesLeft -= SamplesToDo;
353 return SamplesInBuffer;
356 void WriteSamples(ALuint count, short *buffer)
357 { pfluid_synth_write_s16(fluidSynth, count, buffer, 0, 2, buffer, 1, 2); }
358 void WriteSamples(ALuint count, float *buffer)
359 { pfluid_synth_write_float(fluidSynth, count, buffer, 0, 2, buffer, 1, 2); }
361 void ProcessMidi()
363 ALuint newtempo = 0;
365 // Process more events
366 std::vector<MidiTrack>::iterator i=Tracks.begin(), end=Tracks.end();
367 while(i != end)
369 if(i->Offset >= i->data.size() || i->SamplesLeft >= 1.)
371 i++;
372 continue;
375 if(i->data.size() - i->Offset < 3)
377 i->Offset = i->data.size();
378 i++;
379 continue;
382 ALubyte event = i->data[i->Offset++];
383 ALubyte parm1, parm2;
384 if(!(event&0x80))
386 event = i->LastEvent;
387 i->Offset--;
389 if((event&MIDI_EVENT_MASK) != MIDI_SPECIAL)
390 i->LastEvent = event;
391 parm1 = i->data[i->Offset];
392 parm2 = i->data[i->Offset+1];
394 int channel = event&MIDI_CHANNEL_MASK;
395 switch(event&MIDI_EVENT_MASK)
397 case MIDI_NOTEOFF:
398 pfluid_synth_noteoff(fluidSynth, channel, parm1);
399 i->Offset += 2;
400 break;
401 case MIDI_NOTEON:
402 pfluid_synth_noteon(fluidSynth, channel, parm1, parm2);
403 i->Offset += 2;
404 break;
405 case MIDI_POLYPRESS:
406 i->Offset += 2;
407 break;
409 case MIDI_CTRLCHANGE:
410 pfluid_synth_cc(fluidSynth, channel, parm1, parm2);
411 i->Offset += 2;
412 break;
413 case MIDI_PRGMCHANGE:
414 pfluid_synth_program_change(fluidSynth, channel, parm1);
415 i->Offset += 1;
416 break;
418 case MIDI_CHANPRESS:
419 pfluid_synth_channel_pressure(fluidSynth, channel, parm1);
420 i->Offset += 1;
421 break;
423 case MIDI_PITCHBEND:
424 pfluid_synth_pitch_bend(fluidSynth, channel, (parm1&0x7F) | ((parm2&0x7F)<<7));
425 i->Offset += 2;
426 break;
428 case MIDI_SPECIAL:
429 switch(event)
431 case MIDI_SYSEX:
433 unsigned long len = i->ReadVarLen();
434 if(i->data.size() - i->Offset < len)
436 i->Offset = i->data.size();
437 break;
440 if(len > 1 && i->data[len-1] == MIDI_SYSEXEND)
442 char *data = reinterpret_cast<char*>(&i->data[i->Offset]);
443 pfluid_synth_sysex(fluidSynth, data, len-1, NULL, NULL, NULL, false);
445 i->Offset += len;
446 break;
448 case MIDI_SYSEXEND:
450 unsigned long len = i->ReadVarLen();
451 if(i->data.size() - i->Offset < len)
453 i->Offset = i->data.size();
454 break;
456 i->Offset += len;
457 break;
460 case MIDI_SONGPOS:
461 i->Offset += 2;
462 break;
464 case MIDI_SONGSEL:
465 i->Offset += 1;
466 break;
468 case MIDI_META:
470 ALubyte metatype = i->data[i->Offset++];
471 unsigned long val = i->ReadVarLen();
473 if(i->data.size() - i->Offset < val)
475 i->Offset = i->data.size();
476 break;
479 if(metatype == MIDI_META_EOT)
481 i->Offset = i->data.size();
482 break;
485 if(metatype == MIDI_META_TEMPO && val >= 3)
487 newtempo = (i->data[i->Offset] << 16) |
488 (i->data[i->Offset+1] << 8) |
489 (i->data[i->Offset+2]);
492 i->Offset += val;
493 break;
496 default:
497 /* The rest of the special events don't have any
498 * data bytes */
499 break;
501 break;
503 default:
504 /* Shouldn't ever get to here */
505 break;
508 unsigned long val = i->ReadVarLen();
509 i->SamplesLeft += val * samplesPerTick;
511 if(newtempo)
512 UpdateTempo(newtempo);
515 void UpdateTempo(ALuint tempo)
517 ALdouble sampletickrate = sampleRate / (1000000. / tempo) / Divisions;
519 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
520 end = Tracks.end();i != end;i++)
522 if(i->Offset >= i->data.size())
523 continue;
524 i->SamplesLeft = i->SamplesLeft / samplesPerTick * sampletickrate;
526 samplesPerTick = sampletickrate;
529 void SetupSynth()
531 fluidSettings = pnew_fluid_settings();
532 if(fluidSettings)
534 pfluid_settings_setnum(fluidSettings, "synth.gain", 0.5);
535 pfluid_settings_setstr(fluidSettings, "synth.reverb.active", "yes");
536 pfluid_settings_setstr(fluidSettings, "synth.chorus.active", "yes");
537 pfluid_settings_setint(fluidSettings, "synth.polyphony", 256);
538 pfluid_settings_setnum(fluidSettings, "synth.sample-rate", (double)sampleRate);
540 fluidSynth = pnew_fluid_synth(fluidSettings);
541 if(fluidSynth)
543 const char *soundfont = getenv("FLUID_SOUNDFONT");
544 if(soundfont && soundfont[0])
545 fontID = pfluid_synth_sfload(fluidSynth, soundfont, true);
550 static DecoderDecl<fluidStream> fluidStream_decoder;