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 #define FLUIDSYNTH_LIB "libfluidsynth.dll"
41 #elif defined(__APPLE__)
42 #define FLUIDSYNTH_LIB "libfluidsynth.1.dylib"
44 #define FLUIDSYNTH_LIB "libfluidsynth.so.1"
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
);
71 struct fluidStream
: public alureStream
{
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
95 std::vector
<ALubyte
> data
;
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
)
114 const MidiTrack
& operator=(const MidiTrack
&rhs
)
118 LastEvent
= rhs
.LastEvent
;
119 SamplesLeft
= rhs
.SamplesLeft
;
123 unsigned long ReadVarLen()
125 if(Offset
>= data
.size())
128 unsigned long len
= data
[Offset
]&0x7F;
129 while((data
[Offset
]&0x80))
131 if(++Offset
>= data
.size())
133 len
= (len
<<7) | (data
[Offset
]&0x7F);
142 std::vector
<MidiTrack
> Tracks
;
146 ALdouble samplesPerTick
;
148 fluid_settings_t
*fluidSettings
;
149 fluid_synth_t
*fluidSynth
;
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
);
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
;
197 *frequency
= sampleRate
;
198 *blockalign
= 2 * ((format
==AL_FORMAT_STEREO16
) ? sizeof(ALshort
) :
203 virtual ALuint
GetData(ALubyte
*data
, ALuint bytes
)
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
);
215 ALfloat
*ptr
= reinterpret_cast<ALfloat
*>(data
);
216 ret
= FillBuffer(ptr
, bytes
/2/sizeof(ALfloat
));
217 ret
*= 2 * sizeof(ALfloat
);
223 virtual bool Rewind()
225 for(std::vector
<MidiTrack
>::iterator i
= Tracks
.begin(), end
= Tracks
.end();i
!= end
;i
++)
228 unsigned long val
= i
->ReadVarLen();
229 i
->SamplesLeft
+= val
* samplesPerTick
;
231 pfluid_synth_program_reset(fluidSynth
);
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
);
245 SetError("Failed to open file");
249 /* First, get a temp filename */
250 const char *str
= getenv("TEMP");
251 if(!str
|| !str
[0]) str
= getenv("TMP");
253 if(!str
|| !str
[0]) str
= ".";
255 if(!str
|| !str
[0]) str
= "/tmp";
257 std::string fname
= str
;
258 fname
+= "/alure-sfont-XXXXXX";
260 for(size_t i
= 0;i
< fname
.size();i
++)
266 std::vector
<char> tmpfname(fname
.begin(), fname
.end());
267 tmpfname
.push_back(0);
269 /* Open a temp file */
273 if(mktemp(&tmpfname
[0]) == NULL
|| (file
=fopen(&tmpfname
[0], "wb")) == NULL
)
275 if((fd
=mkstemp(&tmpfname
[0])) == -1 || (file
=fdopen(fd
, "wb")) == NULL
)
281 remove(&tmpfname
[0]);
283 SetError("Failed to create temp file");
291 istream
.read(buf
, sizeof(buf
));
292 if((got
=istream
.gcount()) == 0)
297 } while(fwrite(buf
, 1, got
, file
) == got
);
302 newid
= pfluid_synth_sfload(fluidSynth
, &tmpfname
[0], true);
306 remove(&tmpfname
[0]);
310 SetError("Failed to copy file");
314 if(newid
== FLUID_FAILED
)
316 SetError("Failed to load soundfont");
320 if(fontID
!= FLUID_FAILED
)
321 pfluid_synth_sfunload(fluidSynth
, fontID
, 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());
336 alcGetIntegerv(device
, ALC_FREQUENCY
, 1, &sampleRate
);
339 if(!fstream
->read(hdr
, 4))
342 if(memcmp(hdr
, "MThd", 4) == 0)
344 ALuint len
= read_be32(fstream
);
348 int type
= read_be16(fstream
);
349 if(type
!= 0 && type
!= 1)
352 ALuint numtracks
= read_be16(fstream
);
354 Divisions
= read_be16(fstream
);
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)
363 ALint len
= read_be32(fstream
);
365 if(!fstream
->read(reinterpret_cast<char*>(&i
->data
[0]), len
) ||
366 fstream
->gcount() != len
)
369 unsigned long val
= i
->ReadVarLen();
370 i
->SamplesLeft
+= val
* samplesPerTick
;
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
);
386 if(fluidSettings
!= NULL
)
387 pdelete_fluid_settings(fluidSettings
);
388 fluidSettings
= NULL
;
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
);
410 if(TracksPlaying
== 0)
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); }
444 // Process more events
445 std::vector
<MidiTrack
>::iterator i
=Tracks
.begin(), end
=Tracks
.end();
448 if(i
->Offset
>= i
->data
.size() || i
->SamplesLeft
>= 1.)
454 if(i
->data
.size() - i
->Offset
< 3)
456 i
->Offset
= i
->data
.size();
461 ALubyte event
= i
->data
[i
->Offset
++];
462 ALubyte parm1
, parm2
;
465 event
= i
->LastEvent
;
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
)
477 pfluid_synth_noteoff(fluidSynth
, channel
, parm1
);
481 pfluid_synth_noteon(fluidSynth
, channel
, parm1
, parm2
);
488 case MIDI_CTRLCHANGE
:
489 pfluid_synth_cc(fluidSynth
, channel
, parm1
, parm2
);
492 case MIDI_PRGMCHANGE
:
493 pfluid_synth_program_change(fluidSynth
, channel
, parm1
);
498 pfluid_synth_channel_pressure(fluidSynth
, channel
, parm1
);
503 pfluid_synth_pitch_bend(fluidSynth
, channel
, (parm1
&0x7F) | ((parm2
&0x7F)<<7));
512 unsigned long len
= i
->ReadVarLen();
513 if(i
->data
.size() - i
->Offset
< len
)
515 i
->Offset
= i
->data
.size();
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);
529 unsigned long len
= i
->ReadVarLen();
530 if(i
->data
.size() - i
->Offset
< len
)
532 i
->Offset
= i
->data
.size();
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();
558 if(metatype
== MIDI_META_EOT
)
560 i
->Offset
= i
->data
.size();
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]);
576 /* The rest of the special events don't have any
583 /* Shouldn't ever get to here */
587 unsigned long val
= i
->ReadVarLen();
588 i
->SamplesLeft
+= val
* samplesPerTick
;
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())
603 i
->SamplesLeft
= i
->SamplesLeft
/ samplesPerTick
* sampletickrate
;
605 samplesPerTick
= sampletickrate
;
610 fluidSettings
= pnew_fluid_settings();
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
);
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
; }