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
36 #include <fluidsynth.h>
40 static void *fsynth_handle;
41 #define MAKE_FUNC(x) static typeof(x)* p##x
42 MAKE_FUNC(fluid_settings_setstr);
43 MAKE_FUNC(fluid_synth_program_change);
44 MAKE_FUNC(fluid_synth_sfload);
45 MAKE_FUNC(fluid_settings_setnum);
46 MAKE_FUNC(fluid_synth_sysex);
47 MAKE_FUNC(fluid_synth_cc);
48 MAKE_FUNC(fluid_synth_pitch_bend);
49 MAKE_FUNC(fluid_synth_channel_pressure);
50 MAKE_FUNC(fluid_synth_write_float);
51 MAKE_FUNC(new_fluid_synth);
52 MAKE_FUNC(delete_fluid_settings);
53 MAKE_FUNC(delete_fluid_synth);
54 MAKE_FUNC(fluid_synth_program_reset);
55 MAKE_FUNC(fluid_settings_setint);
56 MAKE_FUNC(new_fluid_settings);
57 MAKE_FUNC(fluid_synth_write_s16);
58 MAKE_FUNC(fluid_synth_noteoff);
59 MAKE_FUNC(fluid_synth_sfunload);
60 MAKE_FUNC(fluid_synth_noteon);
63 #define fluid_settings_setstr pfluid_settings_setstr
64 #define fluid_synth_program_change pfluid_synth_program_change
65 #define fluid_synth_sfload pfluid_synth_sfload
66 #define fluid_settings_setnum pfluid_settings_setnum
67 #define fluid_synth_sysex pfluid_synth_sysex
68 #define fluid_synth_cc pfluid_synth_cc
69 #define fluid_synth_pitch_bend pfluid_synth_pitch_bend
70 #define fluid_synth_channel_pressure pfluid_synth_channel_pressure
71 #define fluid_synth_write_float pfluid_synth_write_float
72 #define new_fluid_synth pnew_fluid_synth
73 #define delete_fluid_settings pdelete_fluid_settings
74 #define delete_fluid_synth pdelete_fluid_synth
75 #define fluid_synth_program_reset pfluid_synth_program_reset
76 #define fluid_settings_setint pfluid_settings_setint
77 #define new_fluid_settings pnew_fluid_settings
78 #define fluid_synth_write_s16 pfluid_synth_write_s16
79 #define fluid_synth_noteoff pfluid_synth_noteoff
80 #define fluid_synth_sfunload pfluid_synth_sfunload
81 #define fluid_synth_noteon pfluid_synth_noteon
83 #define fsynth_handle 1
87 struct fluidStream : public alureStream {
89 static const ALubyte MIDI_CHANNEL_MASK = 0x0F;
90 static const ALubyte MIDI_EVENT_MASK = 0xF0;
92 static const ALubyte MIDI_NOTEOFF = 0x80; // + note + velocity
93 static const ALubyte MIDI_NOTEON = 0x90; // + note + velocity
94 static const ALubyte MIDI_POLYPRESS = 0xA0; // + pressure (2 bytes)
95 static const ALubyte MIDI_CTRLCHANGE = 0xB0; // + ctrl + value
96 static const ALubyte MIDI_PRGMCHANGE = 0xC0; // + new patch
97 static const ALubyte MIDI_CHANPRESS = 0xD0; // + pressure (1 byte)
98 static const ALubyte MIDI_PITCHBEND = 0xE0; // + pitch bend (2 bytes)
99 static const ALubyte MIDI_SPECIAL = 0xF0; // Special event
101 static const ALubyte MIDI_SYSEX = 0xF0; // SysEx begin
102 static const ALubyte MIDI_SYSEXEND = 0xF7; // SysEx end
103 static const ALubyte MIDI_SONGPOS = 0xF2; // Song position
104 static const ALubyte MIDI_SONGSEL = 0xF3; // Song select
105 static const ALubyte MIDI_META = 0xFF; // Meta event begin
107 static const ALubyte MIDI_META_EOT = 0x2F; // End-of-track
108 static const ALubyte MIDI_META_TEMPO = 0x51; // Tempo change
111 std::vector<ALubyte> data;
114 ALdouble SamplesLeft;
116 MidiTrack() : Offset(0), LastEvent(0), SamplesLeft(0.)
118 MidiTrack(const MidiTrack &rhs)
119 : data(rhs.data), Offset(rhs.Offset), LastEvent(rhs.LastEvent),
120 SamplesLeft(rhs.SamplesLeft)
130 const MidiTrack& operator=(const MidiTrack &rhs)
134 LastEvent = rhs.LastEvent;
135 SamplesLeft = rhs.SamplesLeft;
139 unsigned long ReadVarLen()
141 if(Offset >= data.size())
144 unsigned long len = data[Offset]&0x7F;
145 while((data[Offset]&0x80))
147 if(++Offset >= data.size())
149 len = (len<<7) | (data[Offset]&0x7F);
158 std::vector<MidiTrack> Tracks;
162 ALdouble samplesPerTick;
164 fluid_settings_t *fluidSettings;
165 fluid_synth_t *fluidSynth;
174 #define FLUIDSYNTH_LIB "libfluidsynth.dll"
175 #elif defined(__APPLE__)
176 #define FLUIDSYNTH_LIB "libfluidsynth.1.dylib"
178 #define FLUIDSYNTH_LIB "libfluidsynth.so.1"
180 fsynth_handle = OpenLib(FLUIDSYNTH_LIB);
181 if(!fsynth_handle) return;
183 LOAD_FUNC(fsynth_handle, fluid_settings_setstr);
184 LOAD_FUNC(fsynth_handle, fluid_synth_program_change);
185 LOAD_FUNC(fsynth_handle, fluid_synth_sfload);
186 LOAD_FUNC(fsynth_handle, fluid_settings_setnum);
187 LOAD_FUNC(fsynth_handle, fluid_synth_sysex);
188 LOAD_FUNC(fsynth_handle, fluid_synth_cc);
189 LOAD_FUNC(fsynth_handle, fluid_synth_pitch_bend);
190 LOAD_FUNC(fsynth_handle, fluid_synth_channel_pressure);
191 LOAD_FUNC(fsynth_handle, fluid_synth_write_float);
192 LOAD_FUNC(fsynth_handle, new_fluid_synth);
193 LOAD_FUNC(fsynth_handle, delete_fluid_settings);
194 LOAD_FUNC(fsynth_handle, delete_fluid_synth);
195 LOAD_FUNC(fsynth_handle, fluid_synth_program_reset);
196 LOAD_FUNC(fsynth_handle, fluid_settings_setint);
197 LOAD_FUNC(fsynth_handle, new_fluid_settings);
198 LOAD_FUNC(fsynth_handle, fluid_synth_write_s16);
199 LOAD_FUNC(fsynth_handle, fluid_synth_noteoff);
200 LOAD_FUNC(fsynth_handle, fluid_synth_sfunload);
201 LOAD_FUNC(fsynth_handle, fluid_synth_noteon);
206 CloseLib(fsynth_handle);
207 fsynth_handle = NULL;
210 static void Init() { }
211 static void Deinit() { }
214 virtual bool IsValid()
215 { return fluidSynth != NULL; }
217 virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
219 if(format == AL_NONE)
221 format = GetSampleFormat(2, 32, true);
222 if(format == AL_NONE)
223 format = AL_FORMAT_STEREO16;
226 *frequency = sampleRate;
227 *blockalign = 2 * ((format==AL_FORMAT_STEREO16) ? sizeof(ALshort) :
232 virtual ALuint GetData(ALubyte *data, ALuint bytes)
239 const char *soundfont = getenv("FLUID_SOUNDFONT");
240 if(soundfont && soundfont[0])
241 fontID = fluid_synth_sfload(fluidSynth, soundfont, true);
244 if(format == AL_FORMAT_STEREO16)
246 ALshort *ptr = reinterpret_cast<ALshort*>(data);
247 ret = FillBuffer(ptr, bytes/2/sizeof(ALshort));
248 ret *= 2 * sizeof(ALshort);
252 ALfloat *ptr = reinterpret_cast<ALfloat*>(data);
253 ret = FillBuffer(ptr, bytes/2/sizeof(ALfloat));
254 ret *= 2 * sizeof(ALfloat);
260 virtual bool Rewind()
262 for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
265 unsigned long val = i->ReadVarLen();
266 i->SamplesLeft += val * samplesPerTick;
268 fluid_synth_program_reset(fluidSynth);
273 virtual bool SetPatchset(const char *sfont)
277 int newid = fluid_synth_sfload(fluidSynth, sfont, true);
278 if(newid == FLUID_FAILED)
280 SetError("Failed to load soundfont");
284 if(fontID != FLUID_FAILED)
285 fluid_synth_sfunload(fluidSynth, fontID, true);
291 /* FluidSynth has no way to load a soundfont using IO callbacks. So we
292 * have to copy the specified file using the callbacks to a regular
293 * file that FluidSynth can open. */
294 int newid = FLUID_FAILED;
295 InStream istream(sfont);
298 SetError("Failed to open file");
302 /* First, get a temp filename */
303 const char *str = getenv("TEMP");
304 if(!str || !str[0]) str = getenv("TMP");
306 if(!str || !str[0]) str = ".";
308 if(!str || !str[0]) str = "/tmp";
310 std::string fname = str;
311 fname += "/alure-sfont-XXXXXX";
313 for(size_t i = 0;i < fname.size();i++)
319 std::vector<char> tmpfname(fname.begin(), fname.end());
320 tmpfname.push_back(0);
322 /* Open a temp file */
326 if(mktemp(&tmpfname[0]) == NULL || (file=fopen(&tmpfname[0], "wb")) == NULL)
328 if((fd=mkstemp(&tmpfname[0])) == -1 || (file=fdopen(fd, "wb")) == NULL)
334 remove(&tmpfname[0]);
336 SetError("Failed to create temp file");
344 istream.read(buf, sizeof(buf));
345 if((got=istream.gcount()) == 0)
350 } while(fwrite(buf, 1, got, file) == got);
355 newid = fluid_synth_sfload(fluidSynth, &tmpfname[0], true);
359 remove(&tmpfname[0]);
363 SetError("Failed to copy file");
367 if(newid == FLUID_FAILED)
369 SetError("Failed to load soundfont");
373 if(fontID != FLUID_FAILED)
374 fluid_synth_sfunload(fluidSynth, fontID, true);
381 fluidStream(std::istream *_fstream)
382 : alureStream(_fstream), Divisions(100),
383 format(AL_NONE), sampleRate(48000), samplesPerTick(1.),
384 fluidSettings(NULL), fluidSynth(NULL), fontID(FLUID_FAILED),
387 if(!fsynth_handle) return;
389 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
390 if(device) alcGetIntegerv(device, ALC_FREQUENCY, 1, &sampleRate);
393 if(!fstream->read(hdr, 4))
396 if(memcmp(hdr, "MThd", 4) == 0)
398 ALuint len = read_be32(fstream);
402 int type = read_be16(fstream);
403 if(type != 0 && type != 1)
406 ALuint numtracks = read_be16(fstream);
408 Divisions = read_be16(fstream);
411 Tracks.resize(numtracks);
412 for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
414 if(!fstream->read(hdr, 4) || memcmp(hdr, "MTrk", 4) != 0)
417 ALint len = read_be32(fstream);
419 if(!fstream->read(reinterpret_cast<char*>(&i->data[0]), len) ||
420 fstream->gcount() != len)
423 unsigned long val = i->ReadVarLen();
424 i->SamplesLeft += val * samplesPerTick;
430 virtual ~fluidStream()
432 if(fontID != FLUID_FAILED)
433 fluid_synth_sfunload(fluidSynth, fontID, true);
434 fontID = FLUID_FAILED;
436 if(fluidSynth != NULL)
437 delete_fluid_synth(fluidSynth);
440 if(fluidSettings != NULL)
441 delete_fluid_settings(fluidSettings);
442 fluidSettings = NULL;
447 ALuint FillBuffer(T *Buffer, ALuint BufferSamples)
449 ALuint SamplesInBuffer = 0;
450 while(SamplesInBuffer < BufferSamples)
452 // Check if any tracks are still playing and how many samples are waiting to render
453 size_t TracksPlaying = 0;
454 ALuint SamplesToDo = BufferSamples - SamplesInBuffer;
455 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
456 end = Tracks.end();i != end;i++)
458 if(i->Offset < i->data.size())
460 SamplesToDo = std::min<ALuint>(SamplesToDo, i->SamplesLeft);
464 if(TracksPlaying == 0)
474 WriteSamples(SamplesToDo, Buffer);
475 Buffer += SamplesToDo*2;
476 SamplesInBuffer += SamplesToDo;
478 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
479 end = Tracks.end();i != end;i++)
481 if(i->Offset < i->data.size())
482 i->SamplesLeft -= SamplesToDo;
486 return SamplesInBuffer;
489 void WriteSamples(ALuint count, short *buffer)
490 { fluid_synth_write_s16(fluidSynth, count, buffer, 0, 2, buffer, 1, 2); }
491 void WriteSamples(ALuint count, float *buffer)
492 { fluid_synth_write_float(fluidSynth, count, buffer, 0, 2, buffer, 1, 2); }
498 // Process more events
499 std::vector<MidiTrack>::iterator i=Tracks.begin(), end=Tracks.end();
502 if(i->Offset >= i->data.size() || i->SamplesLeft >= 1.)
508 if(i->data.size() - i->Offset < 3)
510 i->Offset = i->data.size();
515 ALubyte event = i->data[i->Offset++];
516 ALubyte parm1, parm2;
519 event = i->LastEvent;
522 if((event&MIDI_EVENT_MASK) != MIDI_SPECIAL)
523 i->LastEvent = event;
524 parm1 = i->data[i->Offset];
525 parm2 = i->data[i->Offset+1];
527 int channel = event&MIDI_CHANNEL_MASK;
528 switch(event&MIDI_EVENT_MASK)
531 fluid_synth_noteoff(fluidSynth, channel, parm1);
535 fluid_synth_noteon(fluidSynth, channel, parm1, parm2);
542 case MIDI_CTRLCHANGE:
543 fluid_synth_cc(fluidSynth, channel, parm1, parm2);
546 case MIDI_PRGMCHANGE:
547 fluid_synth_program_change(fluidSynth, channel, parm1);
552 fluid_synth_channel_pressure(fluidSynth, channel, parm1);
557 fluid_synth_pitch_bend(fluidSynth, channel, (parm1&0x7F) | ((parm2&0x7F)<<7));
566 unsigned long len = i->ReadVarLen();
567 if(i->data.size() - i->Offset < len)
569 i->Offset = i->data.size();
573 if(i->data[i->Offset+len-1] == MIDI_SYSEXEND)
575 char *data = reinterpret_cast<char*>(&i->data[i->Offset]);
576 fluid_synth_sysex(fluidSynth, data, len-1, NULL, NULL, NULL, false);
583 unsigned long len = i->ReadVarLen();
584 if(i->data.size() - i->Offset < len)
586 i->Offset = i->data.size();
603 ALubyte metatype = i->data[i->Offset++];
604 unsigned long val = i->ReadVarLen();
606 if(i->data.size() - i->Offset < val)
608 i->Offset = i->data.size();
612 if(metatype == MIDI_META_EOT)
614 i->Offset = i->data.size();
618 if(metatype == MIDI_META_TEMPO && val >= 3)
620 newtempo = (i->data[i->Offset] << 16) |
621 (i->data[i->Offset+1] << 8) |
622 (i->data[i->Offset+2]);
630 /* The rest of the special events don't have any
637 /* Shouldn't ever get to here */
641 unsigned long val = i->ReadVarLen();
642 i->SamplesLeft += val * samplesPerTick;
645 UpdateTempo(newtempo);
648 void UpdateTempo(ALuint tempo)
650 ALdouble sampletickrate = sampleRate / (1000000. / tempo) / Divisions;
652 for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
653 end = Tracks.end();i != end;i++)
655 if(i->Offset >= i->data.size())
657 i->SamplesLeft = i->SamplesLeft / samplesPerTick * sampletickrate;
659 samplesPerTick = sampletickrate;
664 fluidSettings = new_fluid_settings();
667 fluid_settings_setnum(fluidSettings, "synth.gain", 0.5);
668 fluid_settings_setstr(fluidSettings, "synth.reverb.active", "yes");
669 fluid_settings_setstr(fluidSettings, "synth.chorus.active", "yes");
670 fluid_settings_setint(fluidSettings, "synth.polyphony", 256);
671 fluid_settings_setnum(fluidSettings, "synth.sample-rate", (double)sampleRate);
673 fluidSynth = new_fluid_synth(fluidSettings);
677 static DecoderDecl<fluidStream,0> fluidStream_decoder;
678 Decoder &alure_init_fluidsynth(void)
679 { return fluidStream_decoder; }