Update spec for changing license to MIT
[platform/upstream/alure.git] / src / codec_fluidsynth.cpp
1 /*
2  * ALURE  OpenAL utility library
3  * Copyright (c) 2009-2010 by Chris Robinson.
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
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.
22  */
23
24 #include "config.h"
25
26 #include "main.h"
27
28 #include <string.h>
29 #include <assert.h>
30 #ifdef _WIN32
31 #include <io.h>
32 #endif
33
34 #include <istream>
35
36 #include <fluidsynth.h>
37
38
39 #ifdef DYNLOAD
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);
61 #undef MAKE_FUNC
62
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
82 #else
83 #define fsynth_handle 1
84 #endif
85
86
87 struct fluidStream : public alureStream {
88 private:
89     static const ALubyte MIDI_CHANNEL_MASK = 0x0F;
90     static const ALubyte MIDI_EVENT_MASK   = 0xF0;
91
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
100
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
106
107     static const ALubyte MIDI_META_EOT   = 0x2F;    // End-of-track
108     static const ALubyte MIDI_META_TEMPO = 0x51;    // Tempo change
109
110     struct MidiTrack {
111         std::vector<ALubyte> data;
112         size_t Offset;
113         ALubyte LastEvent;
114         ALdouble SamplesLeft;
115
116         MidiTrack() : Offset(0), LastEvent(0), SamplesLeft(0.)
117         { }
118         MidiTrack(const MidiTrack &rhs)
119           : data(rhs.data), Offset(rhs.Offset), LastEvent(rhs.LastEvent),
120             SamplesLeft(rhs.SamplesLeft)
121         { }
122
123         void Reset()
124         {
125             Offset = 0;
126             LastEvent = 0;
127             SamplesLeft = 0.;
128         }
129
130         const MidiTrack& operator=(const MidiTrack &rhs)
131         {
132             data = rhs.data;
133             Offset = rhs.Offset;
134             LastEvent = rhs.LastEvent;
135             SamplesLeft = rhs.SamplesLeft;
136             return *this;
137         }
138
139         unsigned long ReadVarLen()
140         {
141             if(Offset >= data.size())
142                 return 0;
143
144             unsigned long len = data[Offset]&0x7F;
145             while((data[Offset]&0x80))
146             {
147                 if(++Offset >= data.size())
148                     return 0;
149                 len = (len<<7) | (data[Offset]&0x7F);
150             }
151             Offset++;
152
153             return len;
154         }
155     };
156
157     ALuint Divisions;
158     std::vector<MidiTrack> Tracks;
159
160     ALenum format;
161     ALsizei sampleRate;
162     ALdouble samplesPerTick;
163
164     fluid_settings_t *fluidSettings;
165     fluid_synth_t *fluidSynth;
166     int fontID;
167     bool doFontLoad;
168
169 public:
170 #ifdef DYNLOAD
171     static void Init()
172     {
173 #ifdef _WIN32
174 #define FLUIDSYNTH_LIB "libfluidsynth.dll"
175 #elif defined(__APPLE__)
176 #define FLUIDSYNTH_LIB "libfluidsynth.1.dylib"
177 #else
178 #define FLUIDSYNTH_LIB "libfluidsynth.so.1"
179 #endif
180         fsynth_handle = OpenLib(FLUIDSYNTH_LIB);
181         if(!fsynth_handle) return;
182
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);
202     }
203     static void Deinit()
204     {
205         if(fsynth_handle)
206             CloseLib(fsynth_handle);
207         fsynth_handle = NULL;
208     }
209 #else
210     static void Init() { }
211     static void Deinit() { }
212 #endif
213
214     virtual bool IsValid()
215     { return fluidSynth != NULL; }
216
217     virtual bool GetFormat(ALenum *fmt, ALuint *frequency, ALuint *blockalign)
218     {
219         if(format == AL_NONE)
220         {
221             format = GetSampleFormat(2, 32, true);
222             if(format == AL_NONE)
223                 format = AL_FORMAT_STEREO16;
224         }
225         *fmt = format;
226         *frequency = sampleRate;
227         *blockalign = 2 * ((format==AL_FORMAT_STEREO16) ? sizeof(ALshort) :
228                                                           sizeof(ALfloat));
229         return true;
230     }
231
232     virtual ALuint GetData(ALubyte *data, ALuint bytes)
233     {
234         ALuint ret;
235
236         if(doFontLoad)
237         {
238             doFontLoad = false;
239             const char *soundfont = getenv("FLUID_SOUNDFONT");
240             if(soundfont && soundfont[0])
241                 fontID = fluid_synth_sfload(fluidSynth, soundfont, true);
242         }
243
244         if(format == AL_FORMAT_STEREO16)
245         {
246             ALshort *ptr = reinterpret_cast<ALshort*>(data);
247             ret = FillBuffer(ptr, bytes/2/sizeof(ALshort));
248             ret *= 2 * sizeof(ALshort);
249         }
250         else
251         {
252             ALfloat *ptr = reinterpret_cast<ALfloat*>(data);
253             ret = FillBuffer(ptr, bytes/2/sizeof(ALfloat));
254             ret *= 2 * sizeof(ALfloat);
255         }
256
257         return ret;
258     }
259
260     virtual bool Rewind()
261     {
262         for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
263         {
264             i->Reset();
265             unsigned long val = i->ReadVarLen();
266             i->SamplesLeft += val * samplesPerTick;
267         }
268         fluid_synth_program_reset(fluidSynth);
269         UpdateTempo(500000);
270         return true;
271     }
272
273     virtual bool SetPatchset(const char *sfont)
274     {
275         if(UsingSTDIO)
276         {
277             int newid = fluid_synth_sfload(fluidSynth, sfont, true);
278             if(newid == FLUID_FAILED)
279             {
280                 SetError("Failed to load soundfont");
281                 return false;
282             }
283
284             if(fontID != FLUID_FAILED)
285                 fluid_synth_sfunload(fluidSynth, fontID, true);
286             fontID = newid;
287             doFontLoad = false;
288             return true;
289         }
290
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);
296         if(istream.fail())
297         {
298             SetError("Failed to open file");
299             return false;
300         }
301
302         /* First, get a temp filename */
303         const char *str = getenv("TEMP");
304         if(!str || !str[0]) str = getenv("TMP");
305 #ifdef _WIN32
306         if(!str || !str[0]) str = ".";
307 #else
308         if(!str || !str[0]) str = "/tmp";
309 #endif
310         std::string fname = str;
311         fname += "/alure-sfont-XXXXXX";
312
313         for(size_t i = 0;i < fname.size();i++)
314         {
315             if(fname[i] == '\\')
316                 fname[i] = '/';
317         }
318
319         std::vector<char> tmpfname(fname.begin(), fname.end());
320         tmpfname.push_back(0);
321
322         /* Open a temp file */
323         int fd = -1;
324         FILE *file;
325 #ifdef _WIN32
326         if(mktemp(&tmpfname[0]) == NULL || (file=fopen(&tmpfname[0], "wb")) == NULL)
327 #else
328         if((fd=mkstemp(&tmpfname[0])) == -1 || (file=fdopen(fd, "wb")) == NULL)
329 #endif
330         {
331             if(fd >= 0)
332             {
333                 close(fd);
334                 remove(&tmpfname[0]);
335             }
336             SetError("Failed to create temp file");
337             return false;
338         }
339
340         bool copyok = false;
341         char buf[4096];
342         size_t got;
343         do {
344             istream.read(buf, sizeof(buf));
345             if((got=istream.gcount()) == 0)
346             {
347                 copyok = true;
348                 break;
349             }
350         } while(fwrite(buf, 1, got, file) == got);
351
352         if(copyok)
353         {
354             fflush(file);
355             newid = fluid_synth_sfload(fluidSynth, &tmpfname[0], true);
356         }
357
358         fclose(file);
359         remove(&tmpfname[0]);
360
361         if(!copyok)
362         {
363             SetError("Failed to copy file");
364             return false;
365         }
366
367         if(newid == FLUID_FAILED)
368         {
369             SetError("Failed to load soundfont");
370             return false;
371         }
372
373         if(fontID != FLUID_FAILED)
374             fluid_synth_sfunload(fluidSynth, fontID, true);
375         fontID = newid;
376         doFontLoad = false;
377
378         return true;
379     }
380
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),
385         doFontLoad(true)
386     {
387         if(!fsynth_handle) return;
388
389         ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
390         if(device) alcGetIntegerv(device, ALC_FREQUENCY, 1, &sampleRate);
391
392         char hdr[4];
393         if(!fstream->read(hdr, 4))
394             return;
395
396         if(memcmp(hdr, "MThd", 4) == 0)
397         {
398             ALuint len = read_be32(fstream);
399             if(len != 6)
400                 return;
401
402             int type = read_be16(fstream);
403             if(type != 0 && type != 1)
404                 return;
405
406             ALuint numtracks = read_be16(fstream);
407
408             Divisions = read_be16(fstream);
409             UpdateTempo(500000);
410
411             Tracks.resize(numtracks);
412             for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
413             {
414                 if(!fstream->read(hdr, 4) || memcmp(hdr, "MTrk", 4) != 0)
415                     return;
416
417                 ALint len = read_be32(fstream);
418                 i->data.resize(len);
419                 if(!fstream->read(reinterpret_cast<char*>(&i->data[0]), len) ||
420                    fstream->gcount() != len)
421                     return;
422
423                 unsigned long val = i->ReadVarLen();
424                 i->SamplesLeft += val * samplesPerTick;
425             }
426             SetupSynth();
427         }
428     }
429
430     virtual ~fluidStream()
431     {
432         if(fontID != FLUID_FAILED)
433             fluid_synth_sfunload(fluidSynth, fontID, true);
434         fontID = FLUID_FAILED;
435
436         if(fluidSynth != NULL)
437             delete_fluid_synth(fluidSynth);
438         fluidSynth = NULL;
439
440         if(fluidSettings != NULL)
441             delete_fluid_settings(fluidSettings);
442         fluidSettings = NULL;
443     }
444
445 private:
446     template<typename T>
447     ALuint FillBuffer(T *Buffer, ALuint BufferSamples)
448     {
449         ALuint SamplesInBuffer = 0;
450         while(SamplesInBuffer < BufferSamples)
451         {
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++)
457             {
458                 if(i->Offset < i->data.size())
459                 {
460                     SamplesToDo = std::min<ALuint>(SamplesToDo, i->SamplesLeft);
461                     TracksPlaying++;
462                 }
463             }
464             if(TracksPlaying == 0)
465                 break;
466
467             if(SamplesToDo == 0)
468             {
469                 ProcessMidi();
470                 continue;
471             }
472
473             // Render samples
474             WriteSamples(SamplesToDo, Buffer);
475             Buffer += SamplesToDo*2;
476             SamplesInBuffer += SamplesToDo;
477
478             for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
479                                                  end = Tracks.end();i != end;i++)
480             {
481                 if(i->Offset < i->data.size())
482                     i->SamplesLeft -= SamplesToDo;
483             }
484         }
485
486         return SamplesInBuffer;
487     }
488
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); }
493
494     void ProcessMidi()
495     {
496         ALuint newtempo = 0;
497
498         // Process more events
499         std::vector<MidiTrack>::iterator i=Tracks.begin(), end=Tracks.end();
500         while(i != end)
501         {
502             if(i->Offset >= i->data.size() || i->SamplesLeft >= 1.)
503             {
504                 i++;
505                 continue;
506             }
507
508             if(i->data.size() - i->Offset < 3)
509             {
510                 i->Offset = i->data.size();
511                 i++;
512                 continue;
513             }
514
515             ALubyte event = i->data[i->Offset++];
516             ALubyte parm1, parm2;
517             if(!(event&0x80))
518             {
519                 event = i->LastEvent;
520                 i->Offset--;
521             }
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];
526
527             int channel = event&MIDI_CHANNEL_MASK;
528             switch(event&MIDI_EVENT_MASK)
529             {
530                 case MIDI_NOTEOFF:
531                     fluid_synth_noteoff(fluidSynth, channel, parm1);
532                     i->Offset += 2;
533                     break;
534                 case MIDI_NOTEON:
535                     fluid_synth_noteon(fluidSynth, channel, parm1, parm2);
536                     i->Offset += 2;
537                     break;
538                 case MIDI_POLYPRESS:
539                     i->Offset += 2;
540                     break;
541
542                 case MIDI_CTRLCHANGE:
543                     fluid_synth_cc(fluidSynth, channel, parm1, parm2);
544                     i->Offset += 2;
545                     break;
546                 case MIDI_PRGMCHANGE:
547                     fluid_synth_program_change(fluidSynth, channel, parm1);
548                     i->Offset += 1;
549                     break;
550
551                 case MIDI_CHANPRESS:
552                     fluid_synth_channel_pressure(fluidSynth, channel, parm1);
553                     i->Offset += 1;
554                     break;
555
556                 case MIDI_PITCHBEND:
557                     fluid_synth_pitch_bend(fluidSynth, channel, (parm1&0x7F) | ((parm2&0x7F)<<7));
558                     i->Offset += 2;
559                     break;
560
561                 case MIDI_SPECIAL:
562                     switch(event)
563                     {
564                         case MIDI_SYSEX:
565                         {
566                             unsigned long len = i->ReadVarLen();
567                             if(i->data.size() - i->Offset < len)
568                             {
569                                 i->Offset = i->data.size();
570                                 break;
571                             }
572
573                             if(i->data[i->Offset+len-1] == MIDI_SYSEXEND)
574                             {
575                                 char *data = reinterpret_cast<char*>(&i->data[i->Offset]);
576                                 fluid_synth_sysex(fluidSynth, data, len-1, NULL, NULL, NULL, false);
577                             }
578                             i->Offset += len;
579                             break;
580                         }
581                         case MIDI_SYSEXEND:
582                         {
583                             unsigned long len = i->ReadVarLen();
584                             if(i->data.size() - i->Offset < len)
585                             {
586                                 i->Offset = i->data.size();
587                                 break;
588                             }
589                             i->Offset += len;
590                             break;
591                         }
592
593                         case MIDI_SONGPOS:
594                             i->Offset += 2;
595                             break;
596
597                         case MIDI_SONGSEL:
598                             i->Offset += 1;
599                             break;
600
601                         case MIDI_META:
602                         {
603                             ALubyte metatype = i->data[i->Offset++];
604                             unsigned long val = i->ReadVarLen();
605
606                             if(i->data.size() - i->Offset < val)
607                             {
608                                 i->Offset = i->data.size();
609                                 break;
610                             }
611
612                             if(metatype == MIDI_META_EOT)
613                             {
614                                 i->Offset = i->data.size();
615                                 break;
616                             }
617
618                             if(metatype == MIDI_META_TEMPO && val >= 3)
619                             {
620                                 newtempo = (i->data[i->Offset] << 16) |
621                                            (i->data[i->Offset+1] << 8) |
622                                            (i->data[i->Offset+2]);
623                             }
624
625                             i->Offset += val;
626                             break;
627                         }
628
629                         default:
630                             /* The rest of the special events don't have any
631                              * data bytes */
632                             break;
633                     }
634                     break;
635
636                 default:
637                     /* Shouldn't ever get to here */
638                     break;
639             }
640
641             unsigned long val = i->ReadVarLen();
642             i->SamplesLeft += val * samplesPerTick;
643         }
644         if(newtempo)
645             UpdateTempo(newtempo);
646     }
647
648     void UpdateTempo(ALuint tempo)
649     {
650         ALdouble sampletickrate = sampleRate / (1000000. / tempo) / Divisions;
651
652         for(std::vector<MidiTrack>::iterator i = Tracks.begin(),
653                                              end = Tracks.end();i != end;i++)
654         {
655             if(i->Offset >= i->data.size())
656                 continue;
657             i->SamplesLeft = i->SamplesLeft / samplesPerTick * sampletickrate;
658         }
659         samplesPerTick = sampletickrate;
660     }
661
662     void SetupSynth()
663     {
664         fluidSettings = new_fluid_settings();
665         if(fluidSettings)
666         {
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);
672
673             fluidSynth = new_fluid_synth(fluidSettings);
674         }
675     }
676 };
677 static DecoderDecl<fluidStream,0> fluidStream_decoder;
678 Decoder &alure_init_fluidsynth(void)
679 { return fluidStream_decoder; }