Fix soundfont loading when IO callbacks are being used
[alure.git] / src / codec_fluidsynth.cpp
blobc9b9c4d3e7eb60698a34ffb2f5448cd4852bc971
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>
30 #ifdef _WIN32
31 #include <io.h>
32 #endif
34 #include <istream>
36 #include <fluidsynth.h>
39 #ifdef _WIN32
40 #define FLUIDSYNTH_LIB "libfluidsynth.dll"
41 #elif defined(__APPLE__)
42 #define FLUIDSYNTH_LIB "libfluidsynth.1.dylib"
43 #else
44 #define FLUIDSYNTH_LIB "libfluidsynth.so.1"
45 #endif
47 static void *fsynth_handle;
48 #define MAKE_FUNC(x) static typeof(x)* p##x
49 MAKE_FUNC(fluid_settings_setstr);
50 MAKE_FUNC(fluid_synth_program_change);
51 MAKE_FUNC(fluid_synth_sfload);
52 MAKE_FUNC(fluid_settings_setnum);
53 MAKE_FUNC(fluid_synth_sysex);
54 MAKE_FUNC(fluid_synth_cc);
55 MAKE_FUNC(fluid_synth_pitch_bend);
56 MAKE_FUNC(fluid_synth_channel_pressure);
57 MAKE_FUNC(fluid_synth_write_float);
58 MAKE_FUNC(new_fluid_synth);
59 MAKE_FUNC(delete_fluid_settings);
60 MAKE_FUNC(delete_fluid_synth);
61 MAKE_FUNC(fluid_synth_program_reset);
62 MAKE_FUNC(fluid_settings_setint);
63 MAKE_FUNC(new_fluid_settings);
64 MAKE_FUNC(fluid_synth_write_s16);
65 MAKE_FUNC(fluid_synth_noteoff);
66 MAKE_FUNC(fluid_synth_sfunload);
67 MAKE_FUNC(fluid_synth_noteon);
68 #undef MAKE_FUNC
71 struct fluidStream : public alureStream {
72 private:
73 static const ALubyte MIDI_CHANNEL_MASK = 0x0F;
74 static const ALubyte MIDI_EVENT_MASK = 0xF0;
76 static const ALubyte MIDI_NOTEOFF = 0x80; // + note + velocity
77 static const ALubyte MIDI_NOTEON = 0x90; // + note + velocity
78 static const ALubyte MIDI_POLYPRESS = 0xA0; // + pressure (2 bytes)
79 static const ALubyte MIDI_CTRLCHANGE = 0xB0; // + ctrl + value
80 static const ALubyte MIDI_PRGMCHANGE = 0xC0; // + new patch
81 static const ALubyte MIDI_CHANPRESS = 0xD0; // + pressure (1 byte)
82 static const ALubyte MIDI_PITCHBEND = 0xE0; // + pitch bend (2 bytes)
83 static const ALubyte MIDI_SPECIAL = 0xF0; // Special event
85 static const ALubyte MIDI_SYSEX = 0xF0; // SysEx begin
86 static const ALubyte MIDI_SYSEXEND = 0xF7; // SysEx end
87 static const ALubyte MIDI_SONGPOS = 0xF2; // Song position
88 static const ALubyte MIDI_SONGSEL = 0xF3; // Song select
89 static const ALubyte MIDI_META = 0xFF; // Meta event begin
91 static const ALubyte MIDI_META_EOT = 0x2F; // End-of-track
92 static const ALubyte MIDI_META_TEMPO = 0x51; // Tempo change
94 struct MidiTrack {
95 std::vector<ALubyte> data;
96 size_t Offset;
97 ALubyte LastEvent;
98 ALdouble SamplesLeft;
100 MidiTrack() : Offset(0), LastEvent(0), SamplesLeft(0.)
102 MidiTrack(const MidiTrack &rhs)
103 : data(rhs.data), Offset(rhs.Offset), LastEvent(rhs.LastEvent),
104 SamplesLeft(rhs.SamplesLeft)
107 void Reset()
109 Offset = 0;
110 LastEvent = 0;
111 SamplesLeft = 0.;
114 const MidiTrack& operator=(const MidiTrack &rhs)
116 data = rhs.data;
117 Offset = rhs.Offset;
118 LastEvent = rhs.LastEvent;
119 SamplesLeft = rhs.SamplesLeft;
120 return *this;
123 unsigned long ReadVarLen()
125 if(Offset >= data.size())
126 return 0;
128 unsigned long len = data[Offset]&0x7F;
129 while((data[Offset]&0x80))
131 if(++Offset >= data.size())
132 return 0;
133 len = (len<<7) | (data[Offset]&0x7F);
135 Offset++;
137 return len;
141 ALuint Divisions;
142 std::vector<MidiTrack> Tracks;
144 ALenum format;
145 ALsizei sampleRate;
146 ALdouble samplesPerTick;
148 fluid_settings_t *fluidSettings;
149 fluid_synth_t *fluidSynth;
150 int fontID;
152 public:
153 static void Init()
155 fsynth_handle = OpenLib(FLUIDSYNTH_LIB);
156 if(!fsynth_handle) return;
158 LOAD_FUNC(fsynth_handle, fluid_settings_setstr);
159 LOAD_FUNC(fsynth_handle, fluid_synth_program_change);
160 LOAD_FUNC(fsynth_handle, fluid_synth_sfload);
161 LOAD_FUNC(fsynth_handle, fluid_settings_setnum);
162 LOAD_FUNC(fsynth_handle, fluid_synth_sysex);
163 LOAD_FUNC(fsynth_handle, fluid_synth_cc);
164 LOAD_FUNC(fsynth_handle, fluid_synth_pitch_bend);
165 LOAD_FUNC(fsynth_handle, fluid_synth_channel_pressure);
166 LOAD_FUNC(fsynth_handle, fluid_synth_write_float);
167 LOAD_FUNC(fsynth_handle, new_fluid_synth);
168 LOAD_FUNC(fsynth_handle, delete_fluid_settings);
169 LOAD_FUNC(fsynth_handle, delete_fluid_synth);
170 LOAD_FUNC(fsynth_handle, fluid_synth_program_reset);
171 LOAD_FUNC(fsynth_handle, fluid_settings_setint);
172 LOAD_FUNC(fsynth_handle, new_fluid_settings);
173 LOAD_FUNC(fsynth_handle, fluid_synth_write_s16);
174 LOAD_FUNC(fsynth_handle, fluid_synth_noteoff);
175 LOAD_FUNC(fsynth_handle, fluid_synth_sfunload);
176 LOAD_FUNC(fsynth_handle, fluid_synth_noteon);
178 static void Deinit()
180 if(fsynth_handle)
181 CloseLib(fsynth_handle);
182 fsynth_handle = NULL;
185 virtual bool IsValid()
186 { return fluidSynth != NULL; }
188 virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
190 if(format == AL_NONE)
192 format = GetSampleFormat(2, 32, true);
193 if(format == AL_NONE)
194 format = AL_FORMAT_STEREO16;
196 *fmt = format;
197 *frequency = sampleRate;
198 *blockalign = 2 * ((format==AL_FORMAT_STEREO16) ? sizeof(ALshort) :
199 sizeof(ALfloat));
200 return true;
203 virtual ALuint GetData(ALubyte *data, ALuint bytes)
205 ALuint ret;
207 if(format == AL_FORMAT_STEREO16)
209 ALshort *ptr = reinterpret_cast<ALshort*>(data);
210 ret = FillBuffer(ptr, bytes/2/sizeof(ALshort));
211 ret *= 2 * sizeof(ALshort);
213 else
215 ALfloat *ptr = reinterpret_cast<ALfloat*>(data);
216 ret = FillBuffer(ptr, bytes/2/sizeof(ALfloat));
217 ret *= 2 * sizeof(ALfloat);
220 return ret;
223 virtual bool Rewind()
225 for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
227 i->Reset();
228 unsigned long val = i->ReadVarLen();
229 i->SamplesLeft += val * samplesPerTick;
231 pfluid_synth_program_reset(fluidSynth);
232 UpdateTempo(500000);
233 return true;
236 virtual bool SetPatchset(const char *sfont)
238 /* FluidSynth has no way to load a soundfont using IO callbacks. So we
239 * have to copy the specified file using the callbacks to a regular
240 * file that FluidSynth can open. */
241 int newid = FLUID_FAILED;
242 InStream istream(sfont);
243 if(istream.fail())
245 SetError("Failed to open file");
246 return false;
249 /* First, get a temp filename */
250 const char *str = getenv("TEMP");
251 if(!str || !str[0]) str = getenv("TMP");
252 #ifdef _WIN32
253 if(!str || !str[0]) str = ".";
254 #else
255 if(!str || !str[0]) str = "/tmp";
256 #endif
257 std::string fname = str;
258 fname += "/alure-sfont-XXXXXX";
260 for(size_t i = 0;i < fname.size();i++)
262 if(fname[i] == '\\')
263 fname[i] = '/';
266 std::vector<char> tmpfname(fname.begin(), fname.end());
267 tmpfname.push_back(0);
269 /* Open a temp file */
270 int fd = -1;
271 FILE *file;
272 #ifdef _WIN32
273 if(mktemp(&tmpfname[0]) == NULL || (file=fopen(&tmpfname[0], "wb")) == NULL)
274 #else
275 if((fd=mkstemp(&tmpfname[0])) == -1 || (file=fdopen(fd, "wb")) == NULL)
276 #endif
278 if(fd >= 0)
280 close(fd);
281 remove(&tmpfname[0]);
283 SetError("Failed to create temp file");
284 return false;
287 bool copyok = false;
288 char buf[4096];
289 size_t got;
290 do {
291 istream.read(buf, sizeof(buf));
292 if((got=istream.gcount()) == 0)
294 copyok = true;
295 break;
297 } while(fwrite(buf, 1, got, file) == got);
299 if(copyok)
301 fflush(file);
302 newid = pfluid_synth_sfload(fluidSynth, &tmpfname[0], true);
305 fclose(file);
306 remove(&tmpfname[0]);
308 if(!copyok)
310 SetError("Failed to copy file");
311 return false;
314 if(newid == FLUID_FAILED)
316 SetError("Failed to load soundfont");
317 return false;
320 if(fontID != FLUID_FAILED)
321 pfluid_synth_sfunload(fluidSynth, fontID, true);
322 fontID = newid;
324 return true;
327 fluidStream(std::istream *_fstream)
328 : alureStream(_fstream), Divisions(100),
329 format(AL_NONE), sampleRate(48000), samplesPerTick(1.),
330 fluidSettings(NULL), fluidSynth(NULL), fontID(FLUID_FAILED)
332 if(!fsynth_handle) return;
334 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
335 if(device)
336 alcGetIntegerv(device, ALC_FREQUENCY, 1, &sampleRate);
338 char hdr[4];
339 if(!fstream->read(hdr, 4))
340 return;
342 if(memcmp(hdr, "MThd", 4) == 0)
344 ALuint len = read_be32(fstream);
345 if(len != 6)
346 return;
348 int type = read_be16(fstream);
349 if(type != 0 && type != 1)
350 return;
352 ALuint numtracks = read_be16(fstream);
354 Divisions = read_be16(fstream);
355 UpdateTempo(500000);
357 Tracks.resize(numtracks);
358 for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
360 if(!fstream->read(hdr, 4) || memcmp(hdr, "MTrk", 4) != 0)
361 return;
363 ALint len = read_be32(fstream);
364 i->data.resize(len);
365 if(!fstream->read(reinterpret_cast<char*>(&i->data[0]), len) ||
366 fstream->gcount() != len)
367 return;
369 unsigned long val = i->ReadVarLen();
370 i->SamplesLeft += val * samplesPerTick;
372 SetupSynth();
376 virtual ~fluidStream()
378 if(fontID != FLUID_FAILED)
379 pfluid_synth_sfunload(fluidSynth, fontID, true);
380 fontID = FLUID_FAILED;
382 if(fluidSynth != NULL)
383 pdelete_fluid_synth(fluidSynth);
384 fluidSynth = NULL;
386 if(fluidSettings != NULL)
387 pdelete_fluid_settings(fluidSettings);
388 fluidSettings = NULL;
391 private:
392 template<typename T>
393 ALuint FillBuffer(T *Buffer, ALuint BufferSamples)
395 ALuint SamplesInBuffer = 0;
396 while(SamplesInBuffer < BufferSamples)
398 // Check if any tracks are still playing and how many samples are waiting to render
399 size_t TracksPlaying = 0;
400 ALuint SamplesToDo = BufferSamples - SamplesInBuffer;
401 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
402 end = Tracks.end();i != end;i++)
404 if(i->Offset < i->data.size())
406 SamplesToDo = std::min<ALuint>(SamplesToDo, i->SamplesLeft);
407 TracksPlaying++;
410 if(TracksPlaying == 0)
411 break;
413 if(SamplesToDo == 0)
415 ProcessMidi();
416 continue;
419 // Render samples
420 WriteSamples(SamplesToDo, Buffer);
421 Buffer += SamplesToDo*2;
422 SamplesInBuffer += SamplesToDo;
424 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
425 end = Tracks.end();i != end;i++)
427 if(i->Offset < i->data.size())
428 i->SamplesLeft -= SamplesToDo;
432 return SamplesInBuffer;
435 void WriteSamples(ALuint count, short *buffer)
436 { pfluid_synth_write_s16(fluidSynth, count, buffer, 0, 2, buffer, 1, 2); }
437 void WriteSamples(ALuint count, float *buffer)
438 { pfluid_synth_write_float(fluidSynth, count, buffer, 0, 2, buffer, 1, 2); }
440 void ProcessMidi()
442 ALuint newtempo = 0;
444 // Process more events
445 std::vector<MidiTrack>::iterator i=Tracks.begin(), end=Tracks.end();
446 while(i != end)
448 if(i->Offset >= i->data.size() || i->SamplesLeft >= 1.)
450 i++;
451 continue;
454 if(i->data.size() - i->Offset < 3)
456 i->Offset = i->data.size();
457 i++;
458 continue;
461 ALubyte event = i->data[i->Offset++];
462 ALubyte parm1, parm2;
463 if(!(event&0x80))
465 event = i->LastEvent;
466 i->Offset--;
468 if((event&MIDI_EVENT_MASK) != MIDI_SPECIAL)
469 i->LastEvent = event;
470 parm1 = i->data[i->Offset];
471 parm2 = i->data[i->Offset+1];
473 int channel = event&MIDI_CHANNEL_MASK;
474 switch(event&MIDI_EVENT_MASK)
476 case MIDI_NOTEOFF:
477 pfluid_synth_noteoff(fluidSynth, channel, parm1);
478 i->Offset += 2;
479 break;
480 case MIDI_NOTEON:
481 pfluid_synth_noteon(fluidSynth, channel, parm1, parm2);
482 i->Offset += 2;
483 break;
484 case MIDI_POLYPRESS:
485 i->Offset += 2;
486 break;
488 case MIDI_CTRLCHANGE:
489 pfluid_synth_cc(fluidSynth, channel, parm1, parm2);
490 i->Offset += 2;
491 break;
492 case MIDI_PRGMCHANGE:
493 pfluid_synth_program_change(fluidSynth, channel, parm1);
494 i->Offset += 1;
495 break;
497 case MIDI_CHANPRESS:
498 pfluid_synth_channel_pressure(fluidSynth, channel, parm1);
499 i->Offset += 1;
500 break;
502 case MIDI_PITCHBEND:
503 pfluid_synth_pitch_bend(fluidSynth, channel, (parm1&0x7F) | ((parm2&0x7F)<<7));
504 i->Offset += 2;
505 break;
507 case MIDI_SPECIAL:
508 switch(event)
510 case MIDI_SYSEX:
512 unsigned long len = i->ReadVarLen();
513 if(i->data.size() - i->Offset < len)
515 i->Offset = i->data.size();
516 break;
519 if(len > 1 && i->data[len-1] == MIDI_SYSEXEND)
521 char *data = reinterpret_cast<char*>(&i->data[i->Offset]);
522 pfluid_synth_sysex(fluidSynth, data, len-1, NULL, NULL, NULL, false);
524 i->Offset += len;
525 break;
527 case MIDI_SYSEXEND:
529 unsigned long len = i->ReadVarLen();
530 if(i->data.size() - i->Offset < len)
532 i->Offset = i->data.size();
533 break;
535 i->Offset += len;
536 break;
539 case MIDI_SONGPOS:
540 i->Offset += 2;
541 break;
543 case MIDI_SONGSEL:
544 i->Offset += 1;
545 break;
547 case MIDI_META:
549 ALubyte metatype = i->data[i->Offset++];
550 unsigned long val = i->ReadVarLen();
552 if(i->data.size() - i->Offset < val)
554 i->Offset = i->data.size();
555 break;
558 if(metatype == MIDI_META_EOT)
560 i->Offset = i->data.size();
561 break;
564 if(metatype == MIDI_META_TEMPO && val >= 3)
566 newtempo = (i->data[i->Offset] << 16) |
567 (i->data[i->Offset+1] << 8) |
568 (i->data[i->Offset+2]);
571 i->Offset += val;
572 break;
575 default:
576 /* The rest of the special events don't have any
577 * data bytes */
578 break;
580 break;
582 default:
583 /* Shouldn't ever get to here */
584 break;
587 unsigned long val = i->ReadVarLen();
588 i->SamplesLeft += val * samplesPerTick;
590 if(newtempo)
591 UpdateTempo(newtempo);
594 void UpdateTempo(ALuint tempo)
596 ALdouble sampletickrate = sampleRate / (1000000. / tempo) / Divisions;
598 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
599 end = Tracks.end();i != end;i++)
601 if(i->Offset >= i->data.size())
602 continue;
603 i->SamplesLeft = i->SamplesLeft / samplesPerTick * sampletickrate;
605 samplesPerTick = sampletickrate;
608 void SetupSynth()
610 fluidSettings = pnew_fluid_settings();
611 if(fluidSettings)
613 pfluid_settings_setnum(fluidSettings, "synth.gain", 0.5);
614 pfluid_settings_setstr(fluidSettings, "synth.reverb.active", "yes");
615 pfluid_settings_setstr(fluidSettings, "synth.chorus.active", "yes");
616 pfluid_settings_setint(fluidSettings, "synth.polyphony", 256);
617 pfluid_settings_setnum(fluidSettings, "synth.sample-rate", (double)sampleRate);
619 fluidSynth = pnew_fluid_synth(fluidSettings);
620 if(fluidSynth)
622 const char *soundfont = getenv("FLUID_SOUNDFONT");
623 if(soundfont && soundfont[0])
624 fontID = pfluid_synth_sfload(fluidSynth, soundfont, true);
629 static DecoderDecl<fluidStream,0> fluidStream_decoder;
630 Decoder &alure_init_fluidsynth(void)
631 { return fluidStream_decoder; }