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
33 #include <fluidsynth.h>
37 #define FLUIDSYNTH_LIB "libfluidsynth.dll"
38 #elif defined(__APPLE__)
39 #define FLUIDSYNTH_LIB "libfluidsynth.1.dylib"
41 #define FLUIDSYNTH_LIB "libfluidsynth.so.1"
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
);
68 struct fluidStream
: public alureStream
{
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
92 std::vector
<ALubyte
> data
;
97 MidiTrack() : Offset(0), LastEvent(0), SamplesLeft(0.)
99 MidiTrack(const MidiTrack
&rhs
)
100 : data(rhs
.data
), Offset(rhs
.Offset
), LastEvent(rhs
.LastEvent
),
101 SamplesLeft(rhs
.SamplesLeft
)
111 const MidiTrack
& operator=(const MidiTrack
&rhs
)
115 LastEvent
= rhs
.LastEvent
;
116 SamplesLeft
= rhs
.SamplesLeft
;
120 unsigned long ReadVarLen()
122 if(Offset
>= data
.size())
125 unsigned long len
= data
[Offset
]&0x7F;
126 while((data
[Offset
]&0x80))
128 if(++Offset
>= data
.size())
130 len
= (len
<<7) | (data
[Offset
]&0x7F);
139 std::vector
<MidiTrack
> Tracks
;
143 ALdouble samplesPerTick
;
145 fluid_settings_t
*fluidSettings
;
146 fluid_synth_t
*fluidSynth
;
152 fsynth_handle
= OpenLib(FLUIDSYNTH_LIB
);
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
);
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
;
194 *frequency
= sampleRate
;
195 *blockalign
= 2 * ((format
==AL_FORMAT_STEREO16
) ? sizeof(ALshort
) :
200 virtual ALuint
GetData(ALubyte
*data
, ALuint bytes
)
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
);
212 ALfloat
*ptr
= reinterpret_cast<ALfloat
*>(data
);
213 ret
= FillBuffer(ptr
, bytes
/2/sizeof(ALfloat
));
214 ret
*= 2 * sizeof(ALfloat
);
220 virtual bool Rewind()
222 for(std::vector
<MidiTrack
>::iterator i
= Tracks
.begin(), end
= Tracks
.end();i
!= end
;i
++)
225 unsigned long val
= i
->ReadVarLen();
226 i
->SamplesLeft
+= val
* samplesPerTick
;
228 pfluid_synth_program_reset(fluidSynth
);
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");
242 if(fontID
!= FLUID_FAILED
)
243 pfluid_synth_sfunload(fluidSynth
, fontID
, 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());
258 alcGetIntegerv(device
, ALC_FREQUENCY
, 1, &sampleRate
);
261 if(!fstream
->read(hdr
, 4))
264 if(memcmp(hdr
, "MThd", 4) == 0)
266 ALuint len
= read_be32(fstream
);
270 int type
= read_be16(fstream
);
271 if(type
!= 0 && type
!= 1)
274 ALuint numtracks
= read_be16(fstream
);
276 Divisions
= read_be16(fstream
);
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)
285 ALuint len
= read_be32(fstream
);
287 if(!fstream
->read(reinterpret_cast<char*>(&i
->data
[0]), len
))
290 unsigned long val
= i
->ReadVarLen();
291 i
->SamplesLeft
+= val
* samplesPerTick
;
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
);
307 if(fluidSettings
!= NULL
)
308 pdelete_fluid_settings(fluidSettings
);
309 fluidSettings
= NULL
;
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
);
331 if(TracksPlaying
== 0)
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); }
365 // Process more events
366 std::vector
<MidiTrack
>::iterator i
=Tracks
.begin(), end
=Tracks
.end();
369 if(i
->Offset
>= i
->data
.size() || i
->SamplesLeft
>= 1.)
375 if(i
->data
.size() - i
->Offset
< 3)
377 i
->Offset
= i
->data
.size();
382 ALubyte event
= i
->data
[i
->Offset
++];
383 ALubyte parm1
, parm2
;
386 event
= i
->LastEvent
;
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
)
398 pfluid_synth_noteoff(fluidSynth
, channel
, parm1
);
402 pfluid_synth_noteon(fluidSynth
, channel
, parm1
, parm2
);
409 case MIDI_CTRLCHANGE
:
410 pfluid_synth_cc(fluidSynth
, channel
, parm1
, parm2
);
413 case MIDI_PRGMCHANGE
:
414 pfluid_synth_program_change(fluidSynth
, channel
, parm1
);
419 pfluid_synth_channel_pressure(fluidSynth
, channel
, parm1
);
424 pfluid_synth_pitch_bend(fluidSynth
, channel
, (parm1
&0x7F) | ((parm2
&0x7F)<<7));
433 unsigned long len
= i
->ReadVarLen();
434 if(i
->data
.size() - i
->Offset
< len
)
436 i
->Offset
= i
->data
.size();
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);
450 unsigned long len
= i
->ReadVarLen();
451 if(i
->data
.size() - i
->Offset
< len
)
453 i
->Offset
= i
->data
.size();
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();
479 if(metatype
== MIDI_META_EOT
)
481 i
->Offset
= i
->data
.size();
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]);
497 /* The rest of the special events don't have any
504 /* Shouldn't ever get to here */
508 unsigned long val
= i
->ReadVarLen();
509 i
->SamplesLeft
+= val
* samplesPerTick
;
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())
524 i
->SamplesLeft
= i
->SamplesLeft
/ samplesPerTick
* sampletickrate
;
526 samplesPerTick
= sampletickrate
;
531 fluidSettings
= pnew_fluid_settings();
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
);
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
;