ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

fluidsynth.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/scummsys.h"
00024 
00025 #ifdef USE_FLUIDSYNTH
00026 
00027 #include "common/config-manager.h"
00028 #include "common/error.h"
00029 #include "common/system.h"
00030 #include "common/textconsole.h"
00031 #include "audio/musicplugin.h"
00032 #include "audio/mpu401.h"
00033 #include "audio/softsynth/emumidi.h"
00034 #if defined(IPHONE_IOS7) && defined(IPHONE_SANDBOXED)
00035 #include "backends/platform/ios7/ios7_common.h"
00036 #endif
00037 
00038 #include <fluidsynth.h>
00039 
00040 class MidiDriver_FluidSynth : public MidiDriver_Emulated {
00041 private:
00042     MidiChannel_MPU401 _midiChannels[16];
00043     fluid_settings_t *_settings;
00044     fluid_synth_t *_synth;
00045     int _soundFont;
00046     int _outputRate;
00047 
00048 protected:
00049     // Because GCC complains about casting from const to non-const...
00050     void setInt(const char *name, int val);
00051     void setNum(const char *name, double num);
00052     void setStr(const char *name, const char *str);
00053 
00054     void generateSamples(int16 *buf, int len);
00055 
00056 public:
00057     MidiDriver_FluidSynth(Audio::Mixer *mixer);
00058 
00059     int open();
00060     void close();
00061     void send(uint32 b);
00062 
00063     MidiChannel *allocateChannel();
00064     MidiChannel *getPercussionChannel();
00065 
00066     // AudioStream API
00067     bool isStereo() const { return true; }
00068     int getRate() const { return _outputRate; }
00069 };
00070 
00071 // MidiDriver method implementations
00072 
00073 MidiDriver_FluidSynth::MidiDriver_FluidSynth(Audio::Mixer *mixer)
00074     : MidiDriver_Emulated(mixer) {
00075 
00076     for (int i = 0; i < ARRAYSIZE(_midiChannels); i++) {
00077         _midiChannels[i].init(this, i);
00078     }
00079 
00080     // It ought to be possible to get FluidSynth to generate samples at
00081     // lower
00082 
00083     _outputRate = _mixer->getOutputRate();
00084     if (_outputRate < 22050)
00085         _outputRate = 22050;
00086     else if (_outputRate > 96000)
00087         _outputRate = 96000;
00088 }
00089 
00090 // The string duplication below is there only because older versions (1.1.6
00091 // and earlier?) of FluidSynth expected the string parameters to be non-const.
00092 
00093 void MidiDriver_FluidSynth::setInt(const char *name, int val) {
00094     char *name2 = scumm_strdup(name);
00095 
00096     fluid_settings_setint(_settings, name2, val);
00097     delete[] name2;
00098 }
00099 
00100 void MidiDriver_FluidSynth::setNum(const char *name, double val) {
00101     char *name2 = scumm_strdup(name);
00102 
00103     fluid_settings_setnum(_settings, name2, val);
00104     delete[] name2;
00105 }
00106 
00107 void MidiDriver_FluidSynth::setStr(const char *name, const char *val) {
00108     char *name2 = scumm_strdup(name);
00109     char *val2 = scumm_strdup(val);
00110 
00111     fluid_settings_setstr(_settings, name2, val2);
00112     delete[] name2;
00113     delete[] val2;
00114 }
00115 
00116 int MidiDriver_FluidSynth::open() {
00117     if (_isOpen)
00118         return MERR_ALREADY_OPEN;
00119 
00120     if (!ConfMan.hasKey("soundfont"))
00121         error("FluidSynth requires a 'soundfont' setting");
00122 
00123     _settings = new_fluid_settings();
00124 
00125     // The default gain setting is ridiculously low - at least for me. This
00126     // cannot be fixed by ScummVM's volume settings because they can only
00127     // soften the sound, not amplify it, so instead we add an option to
00128     // adjust the gain of FluidSynth itself.
00129 
00130     double gain = (double)ConfMan.getInt("midi_gain") / 100.0;
00131 
00132     setNum("synth.gain", gain);
00133     setNum("synth.sample-rate", _outputRate);
00134 
00135     _synth = new_fluid_synth(_settings);
00136 
00137     if (ConfMan.getBool("fluidsynth_chorus_activate")) {
00138         fluid_synth_set_chorus_on(_synth, 1);
00139 
00140         int chorusNr = ConfMan.getInt("fluidsynth_chorus_nr");
00141         double chorusLevel = (double)ConfMan.getInt("fluidsynth_chorus_level") / 100.0;
00142         double chorusSpeed = (double)ConfMan.getInt("fluidsynth_chorus_speed") / 100.0;
00143         double chorusDepthMs = (double)ConfMan.getInt("fluidsynth_chorus_depth") / 10.0;
00144 
00145         Common::String chorusWaveForm = ConfMan.get("fluidsynth_chorus_waveform");
00146         int chorusType = FLUID_CHORUS_MOD_SINE;
00147         if (chorusWaveForm == "sine") {
00148             chorusType = FLUID_CHORUS_MOD_SINE;
00149         } else {
00150             chorusType = FLUID_CHORUS_MOD_TRIANGLE;
00151         }
00152 
00153         fluid_synth_set_chorus(_synth, chorusNr, chorusLevel, chorusSpeed, chorusDepthMs, chorusType);
00154     } else {
00155         fluid_synth_set_chorus_on(_synth, 0);
00156     }
00157 
00158     if (ConfMan.getBool("fluidsynth_reverb_activate")) {
00159         fluid_synth_set_reverb_on(_synth, 1);
00160 
00161         double reverbRoomSize = (double)ConfMan.getInt("fluidsynth_reverb_roomsize") / 100.0;
00162         double reverbDamping = (double)ConfMan.getInt("fluidsynth_reverb_damping") / 100.0;
00163         int reverbWidth = ConfMan.getInt("fluidsynth_reverb_width");
00164         double reverbLevel = (double)ConfMan.getInt("fluidsynth_reverb_level") / 100.0;
00165 
00166         fluid_synth_set_reverb(_synth, reverbRoomSize, reverbDamping, reverbWidth, reverbLevel);
00167     } else {
00168         fluid_synth_set_reverb_on(_synth, 0);
00169     }
00170 
00171     Common::String interpolation = ConfMan.get("fluidsynth_misc_interpolation");
00172     int interpMethod = FLUID_INTERP_4THORDER;
00173 
00174     if (interpolation == "none") {
00175         interpMethod = FLUID_INTERP_NONE;
00176     } else if (interpolation == "linear") {
00177         interpMethod = FLUID_INTERP_LINEAR;
00178     } else if (interpolation == "4th") {
00179         interpMethod = FLUID_INTERP_4THORDER;
00180     } else if (interpolation == "7th") {
00181         interpMethod = FLUID_INTERP_7THORDER;
00182     }
00183 
00184     fluid_synth_set_interp_method(_synth, -1, interpMethod);
00185 
00186     const char *soundfont = ConfMan.get("soundfont").c_str();
00187 
00188 #if defined(IPHONE_IOS7) && defined(IPHONE_SANDBOXED)
00189     // HACK: Due to the sandbox on non-jailbroken iOS devices, we need to deal
00190     // with the chroot filesystem. All the path selected by the user are
00191     // relative to the Document directory. So, we need to adjust the path to
00192     // reflect that.
00193     Common::String soundfont_fullpath = iOS7_getDocumentsDir();
00194     soundfont_fullpath += soundfont;
00195     _soundFont = fluid_synth_sfload(_synth, soundfont_fullpath.c_str(), 1);
00196 #else
00197     _soundFont = fluid_synth_sfload(_synth, soundfont, 1);
00198 #endif
00199 
00200     if (_soundFont == -1)
00201         error("Failed loading custom sound font '%s'", soundfont);
00202 
00203     MidiDriver_Emulated::open();
00204 
00205     _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
00206     return 0;
00207 }
00208 
00209 void MidiDriver_FluidSynth::close() {
00210     if (!_isOpen)
00211         return;
00212     _isOpen = false;
00213 
00214     _mixer->stopHandle(_mixerSoundHandle);
00215 
00216     if (_soundFont != -1)
00217         fluid_synth_sfunload(_synth, _soundFont, 1);
00218 
00219     delete_fluid_synth(_synth);
00220     delete_fluid_settings(_settings);
00221 }
00222 
00223 void MidiDriver_FluidSynth::send(uint32 b) {
00224     //byte param3 = (byte) ((b >> 24) & 0xFF);
00225     uint param2 = (byte) ((b >> 16) & 0xFF);
00226     uint param1 = (byte) ((b >>  8) & 0xFF);
00227     byte cmd    = (byte) (b & 0xF0);
00228     byte chan   = (byte) (b & 0x0F);
00229 
00230     switch (cmd) {
00231     case 0x80:  // Note Off
00232         fluid_synth_noteoff(_synth, chan, param1);
00233         break;
00234     case 0x90:  // Note On
00235         fluid_synth_noteon(_synth, chan, param1, param2);
00236         break;
00237     case 0xA0:  // Aftertouch
00238         break;
00239     case 0xB0:  // Control Change
00240         fluid_synth_cc(_synth, chan, param1, param2);
00241         break;
00242     case 0xC0:  // Program Change
00243         fluid_synth_program_change(_synth, chan, param1);
00244         break;
00245     case 0xD0:  // Channel Pressure
00246         break;
00247     case 0xE0:  // Pitch Bend
00248         fluid_synth_pitch_bend(_synth, chan, (param2 << 7) | param1);
00249         break;
00250     case 0xF0:  // SysEx
00251         // We should never get here! SysEx information has to be
00252         // sent via high-level semantic methods.
00253         warning("MidiDriver_FluidSynth: Receiving SysEx command on a send() call");
00254         break;
00255     default:
00256         warning("MidiDriver_FluidSynth: Unknown send() command 0x%02X", cmd);
00257         break;
00258     }
00259 }
00260 
00261 MidiChannel *MidiDriver_FluidSynth::allocateChannel() {
00262     for (int i = 0; i < ARRAYSIZE(_midiChannels); i++) {
00263         if (i != 9 && _midiChannels[i].allocate())
00264             return &_midiChannels[i];
00265     }
00266     return NULL;
00267 }
00268 
00269 MidiChannel *MidiDriver_FluidSynth::getPercussionChannel() {
00270     return &_midiChannels[9];
00271 }
00272 
00273 void MidiDriver_FluidSynth::generateSamples(int16 *data, int len) {
00274     fluid_synth_write_s16(_synth, len, data, 0, 2, data, 1, 2);
00275 }
00276 
00277 
00278 // Plugin interface
00279 
00280 class FluidSynthMusicPlugin : public MusicPluginObject {
00281 public:
00282     const char *getName() const {
00283         return "FluidSynth";
00284     }
00285 
00286     const char *getId() const {
00287         return "fluidsynth";
00288     }
00289 
00290     MusicDevices getDevices() const;
00291     Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const;
00292 };
00293 
00294 MusicDevices FluidSynthMusicPlugin::getDevices() const {
00295     MusicDevices devices;
00296     devices.push_back(MusicDevice(this, "", MT_GM));
00297     return devices;
00298 }
00299 
00300 Common::Error FluidSynthMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
00301     *mididriver = new MidiDriver_FluidSynth(g_system->getMixer());
00302 
00303     return Common::kNoError;
00304 }
00305 
00306 //#if PLUGIN_ENABLED_DYNAMIC(FLUIDSYNTH)
00307     //REGISTER_PLUGIN_DYNAMIC(FLUIDSYNTH, PLUGIN_TYPE_MUSIC, FluidSynthMusicPlugin);
00308 //#else
00309     REGISTER_PLUGIN_STATIC(FLUIDSYNTH, PLUGIN_TYPE_MUSIC, FluidSynthMusicPlugin);
00310 //#endif
00311 
00312 #endif


Generated on Sat May 18 2019 05:01:02 for ResidualVM by doxygen 1.7.1
curved edge   curved edge