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
38 #include <fluidsynth.h>
41 struct fluidStream
: public alureStream
{
43 static const ALubyte MIDI_CHANNEL_MASK
= 0x0F;
44 static const ALubyte MIDI_EVENT_MASK
= 0xF0;
46 static const ALubyte MIDI_NOTEOFF
= 0x80; // + note + velocity
47 static const ALubyte MIDI_NOTEON
= 0x90; // + note + velocity
48 static const ALubyte MIDI_POLYPRESS
= 0xA0; // + pressure (2 bytes)
49 static const ALubyte MIDI_CTRLCHANGE
= 0xB0; // + ctrl + value
50 static const ALubyte MIDI_PRGMCHANGE
= 0xC0; // + new patch
51 static const ALubyte MIDI_CHANPRESS
= 0xD0; // + pressure (1 byte)
52 static const ALubyte MIDI_PITCHBEND
= 0xE0; // + pitch bend (2 bytes)
53 static const ALubyte MIDI_SPECIAL
= 0xF0; // Special event
55 static const ALubyte MIDI_SYSEX
= 0xF0; // SysEx begin
56 static const ALubyte MIDI_SYSEXEND
= 0xF7; // SysEx end
57 static const ALubyte MIDI_SONGPOS
= 0xF2; // Song position
58 static const ALubyte MIDI_SONGSEL
= 0xF3; // Song select
59 static const ALubyte MIDI_META
= 0xFF; // Meta event begin
61 static const ALubyte MIDI_META_EOT
= 0x2F; // End-of-track
62 static const ALubyte MIDI_META_TEMPO
= 0x51; // Tempo change
65 std::vector
<ALubyte
> data
;
70 MidiTrack() : Offset(0), LastEvent(0), SamplesLeft(0.)
72 MidiTrack(const MidiTrack
&rhs
)
73 : data(rhs
.data
), Offset(rhs
.Offset
), LastEvent(rhs
.LastEvent
),
74 SamplesLeft(rhs
.SamplesLeft
)
84 const MidiTrack
& operator=(const MidiTrack
&rhs
)
88 LastEvent
= rhs
.LastEvent
;
89 SamplesLeft
= rhs
.SamplesLeft
;
93 unsigned long ReadVarLen()
95 if(Offset
>= data
.size())
98 unsigned long len
= data
[Offset
]&0x7F;
99 while((data
[Offset
]&0x80))
101 if(++Offset
>= data
.size())
103 len
= (len
<<7) | (data
[Offset
]&0x7F);
112 std::vector
<MidiTrack
> Tracks
;
116 ALdouble samplesPerTick
;
118 fluid_settings_t
*fluidSettings
;
119 fluid_synth_t
*fluidSynth
;
124 static void Init() { }
125 static void Deinit() { }
127 virtual bool IsValid()
128 { return fluidSynth
!= NULL
; }
130 virtual bool GetFormat(ALenum
*fmt
, ALuint
*frequency
, ALuint
*blockalign
)
132 if(format
== AL_NONE
)
134 format
= GetSampleFormat(2, 32, true);
135 if(format
== AL_NONE
)
136 format
= AL_FORMAT_STEREO16
;
139 *frequency
= sampleRate
;
140 *blockalign
= 2 * ((format
==AL_FORMAT_STEREO16
) ? sizeof(ALshort
) :
145 virtual ALuint
GetData(ALubyte
*data
, ALuint bytes
)
152 const char *soundfont
= getenv("FLUID_SOUNDFONT");
153 if(soundfont
&& soundfont
[0])
154 fontID
= fluid_synth_sfload(fluidSynth
, soundfont
, true);
157 if(format
== AL_FORMAT_STEREO16
)
159 ALshort
*ptr
= reinterpret_cast<ALshort
*>(data
);
160 ret
= FillBuffer(ptr
, bytes
/2/sizeof(ALshort
));
161 ret
*= 2 * sizeof(ALshort
);
165 ALfloat
*ptr
= reinterpret_cast<ALfloat
*>(data
);
166 ret
= FillBuffer(ptr
, bytes
/2/sizeof(ALfloat
));
167 ret
*= 2 * sizeof(ALfloat
);
173 virtual bool Rewind()
175 for(std::vector
<MidiTrack
>::iterator i
= Tracks
.begin(), end
= Tracks
.end();i
!= end
;i
++)
178 unsigned long val
= i
->ReadVarLen();
179 i
->SamplesLeft
+= val
* samplesPerTick
;
181 fluid_synth_program_reset(fluidSynth
);
186 virtual bool SetPatchset(const char *sfont
)
190 int newid
= fluid_synth_sfload(fluidSynth
, sfont
, true);
191 if(newid
== FLUID_FAILED
)
193 SetError("Failed to load soundfont");
197 if(fontID
!= FLUID_FAILED
)
198 fluid_synth_sfunload(fluidSynth
, fontID
, true);
204 /* FluidSynth has no way to load a soundfont using IO callbacks. So we
205 * have to copy the specified file using the callbacks to a regular
206 * file that FluidSynth can open. */
207 int newid
= FLUID_FAILED
;
208 InStream
istream(sfont
);
211 SetError("Failed to open file");
215 /* First, get a temp filename */
216 const char *str
= getenv("TEMP");
217 if(!str
|| !str
[0]) str
= getenv("TMP");
219 if(!str
|| !str
[0]) str
= ".";
221 if(!str
|| !str
[0]) str
= "/tmp";
223 std::string fname
= str
;
224 fname
+= "/alure-sfont-XXXXXX";
226 for(size_t i
= 0;i
< fname
.size();i
++)
232 std::vector
<char> tmpfname(fname
.begin(), fname
.end());
233 tmpfname
.push_back(0);
235 /* Open a temp file */
239 if(mktemp(&tmpfname
[0]) == NULL
|| (file
=fopen(&tmpfname
[0], "wb")) == NULL
)
241 if((fd
=mkstemp(&tmpfname
[0])) == -1 || (file
=fdopen(fd
, "wb")) == NULL
)
247 remove(&tmpfname
[0]);
249 SetError("Failed to create temp file");
257 istream
.read(buf
, sizeof(buf
));
258 if((got
=istream
.gcount()) == 0)
263 } while(fwrite(buf
, 1, got
, file
) == got
);
268 newid
= fluid_synth_sfload(fluidSynth
, &tmpfname
[0], true);
272 remove(&tmpfname
[0]);
276 SetError("Failed to copy file");
280 if(newid
== FLUID_FAILED
)
282 SetError("Failed to load soundfont");
286 if(fontID
!= FLUID_FAILED
)
287 fluid_synth_sfunload(fluidSynth
, fontID
, true);
294 fluidStream(std::istream
*_fstream
)
295 : alureStream(_fstream
), Divisions(100),
296 format(AL_NONE
), sampleRate(48000), samplesPerTick(1.),
297 fluidSettings(NULL
), fluidSynth(NULL
), fontID(FLUID_FAILED
),
300 ALCdevice
*device
= alcGetContextsDevice(alcGetCurrentContext());
301 if(device
) alcGetIntegerv(device
, ALC_FREQUENCY
, 1, &sampleRate
);
304 if(!fstream
->read(hdr
, 4))
307 if(memcmp(hdr
, "MThd", 4) == 0)
309 ALuint len
= read_be32(fstream
);
313 int type
= read_be16(fstream
);
314 if(type
!= 0 && type
!= 1)
317 ALuint numtracks
= read_be16(fstream
);
319 Divisions
= read_be16(fstream
);
322 Tracks
.resize(numtracks
);
323 for(std::vector
<MidiTrack
>::iterator i
= Tracks
.begin(), end
= Tracks
.end();i
!= end
;i
++)
325 if(!fstream
->read(hdr
, 4) || memcmp(hdr
, "MTrk", 4) != 0)
328 ALint len
= read_be32(fstream
);
330 if(!fstream
->read(reinterpret_cast<char*>(&i
->data
[0]), len
) ||
331 fstream
->gcount() != len
)
334 unsigned long val
= i
->ReadVarLen();
335 i
->SamplesLeft
+= val
* samplesPerTick
;
341 virtual ~fluidStream()
343 if(fontID
!= FLUID_FAILED
)
344 fluid_synth_sfunload(fluidSynth
, fontID
, true);
345 fontID
= FLUID_FAILED
;
347 if(fluidSynth
!= NULL
)
348 delete_fluid_synth(fluidSynth
);
351 if(fluidSettings
!= NULL
)
352 delete_fluid_settings(fluidSettings
);
353 fluidSettings
= NULL
;
358 ALuint
FillBuffer(T
*Buffer
, ALuint BufferSamples
)
360 ALuint SamplesInBuffer
= 0;
361 while(SamplesInBuffer
< BufferSamples
)
363 // Check if any tracks are still playing and how many samples are waiting to render
364 size_t TracksPlaying
= 0;
365 ALuint SamplesToDo
= BufferSamples
- SamplesInBuffer
;
366 for(std::vector
<MidiTrack
>::iterator i
= Tracks
.begin(),
367 end
= Tracks
.end();i
!= end
;i
++)
369 if(i
->Offset
< i
->data
.size())
371 SamplesToDo
= std::min
<ALuint
>(SamplesToDo
, i
->SamplesLeft
);
375 if(TracksPlaying
== 0)
385 WriteSamples(SamplesToDo
, Buffer
);
386 Buffer
+= SamplesToDo
*2;
387 SamplesInBuffer
+= SamplesToDo
;
389 for(std::vector
<MidiTrack
>::iterator i
= Tracks
.begin(),
390 end
= Tracks
.end();i
!= end
;i
++)
392 if(i
->Offset
< i
->data
.size())
393 i
->SamplesLeft
-= SamplesToDo
;
397 return SamplesInBuffer
;
400 void WriteSamples(ALuint count
, short *buffer
)
401 { fluid_synth_write_s16(fluidSynth
, count
, buffer
, 0, 2, buffer
, 1, 2); }
402 void WriteSamples(ALuint count
, float *buffer
)
403 { fluid_synth_write_float(fluidSynth
, count
, buffer
, 0, 2, buffer
, 1, 2); }
409 // Process more events
410 std::vector
<MidiTrack
>::iterator i
=Tracks
.begin(), end
=Tracks
.end();
413 if(i
->Offset
>= i
->data
.size() || i
->SamplesLeft
>= 1.)
419 if(i
->data
.size() - i
->Offset
< 3)
421 i
->Offset
= i
->data
.size();
426 ALubyte event
= i
->data
[i
->Offset
++];
427 ALubyte parm1
, parm2
;
430 event
= i
->LastEvent
;
433 if((event
&MIDI_EVENT_MASK
) != MIDI_SPECIAL
)
434 i
->LastEvent
= event
;
435 parm1
= i
->data
[i
->Offset
];
436 parm2
= i
->data
[i
->Offset
+1];
438 int channel
= event
&MIDI_CHANNEL_MASK
;
439 switch(event
&MIDI_EVENT_MASK
)
442 fluid_synth_noteoff(fluidSynth
, channel
, parm1
);
446 fluid_synth_noteon(fluidSynth
, channel
, parm1
, parm2
);
453 case MIDI_CTRLCHANGE
:
454 fluid_synth_cc(fluidSynth
, channel
, parm1
, parm2
);
457 case MIDI_PRGMCHANGE
:
458 fluid_synth_program_change(fluidSynth
, channel
, parm1
);
463 fluid_synth_channel_pressure(fluidSynth
, channel
, parm1
);
468 fluid_synth_pitch_bend(fluidSynth
, channel
, (parm1
&0x7F) | ((parm2
&0x7F)<<7));
477 unsigned long len
= i
->ReadVarLen();
478 if(i
->data
.size() - i
->Offset
< len
)
480 i
->Offset
= i
->data
.size();
484 if(i
->data
[i
->Offset
+len
-1] == MIDI_SYSEXEND
)
486 char *data
= reinterpret_cast<char*>(&i
->data
[i
->Offset
]);
487 fluid_synth_sysex(fluidSynth
, data
, len
-1, NULL
, NULL
, NULL
, false);
494 unsigned long len
= i
->ReadVarLen();
495 if(i
->data
.size() - i
->Offset
< len
)
497 i
->Offset
= i
->data
.size();
514 ALubyte metatype
= i
->data
[i
->Offset
++];
515 unsigned long val
= i
->ReadVarLen();
517 if(i
->data
.size() - i
->Offset
< val
)
519 i
->Offset
= i
->data
.size();
523 if(metatype
== MIDI_META_EOT
)
525 i
->Offset
= i
->data
.size();
529 if(metatype
== MIDI_META_TEMPO
&& val
>= 3)
531 newtempo
= (i
->data
[i
->Offset
] << 16) |
532 (i
->data
[i
->Offset
+1] << 8) |
533 (i
->data
[i
->Offset
+2]);
541 /* The rest of the special events don't have any
548 /* Shouldn't ever get to here */
552 unsigned long val
= i
->ReadVarLen();
553 i
->SamplesLeft
+= val
* samplesPerTick
;
556 UpdateTempo(newtempo
);
559 void UpdateTempo(ALuint tempo
)
561 ALdouble sampletickrate
= sampleRate
/ (1000000. / tempo
) / Divisions
;
563 for(std::vector
<MidiTrack
>::iterator i
= Tracks
.begin(),
564 end
= Tracks
.end();i
!= end
;i
++)
566 if(i
->Offset
>= i
->data
.size())
568 i
->SamplesLeft
= i
->SamplesLeft
/ samplesPerTick
* sampletickrate
;
570 samplesPerTick
= sampletickrate
;
575 fluidSettings
= new_fluid_settings();
578 fluid_settings_setnum(fluidSettings
, "synth.gain", 0.5);
579 fluid_settings_setstr(fluidSettings
, "synth.reverb.active", "yes");
580 fluid_settings_setstr(fluidSettings
, "synth.chorus.active", "yes");
581 fluid_settings_setint(fluidSettings
, "synth.polyphony", 256);
582 fluid_settings_setnum(fluidSettings
, "synth.sample-rate", (double)sampleRate
);
584 fluidSynth
= new_fluid_synth(fluidSettings
);
588 static DecoderDecl
<fluidStream
,0> fluidStream_decoder
;
589 Decoder
&alure_init_fluidsynth(void)
590 { return fluidStream_decoder
; }