| 1 | /* ScummVM - Scumm Interpreter |
| 2 | * Copyright (C) 2001-2005 The ScummVM project |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License |
| 6 | * as published by the Free Software Foundation; either version 2 |
| 7 | * of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program; if not, write to the Free Software |
| 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 17 | * |
| 18 | * $Header$ |
| 19 | */ |
| 20 | |
| 21 | #if defined(HAVE_CONFIG_H) |
| 22 | #include "config.h" |
| 23 | #endif |
| 24 | |
| 25 | #ifdef USE_FLUIDSYNTH |
| 26 | |
| 27 | #include "stdafx.h" |
| 28 | #include "common/config-manager.h" |
| 29 | #include "sound/mpu401.h" |
| 30 | #include "sound/softsynth/emumidi.h" |
| 31 | |
| 32 | #include <fluidsynth.h> |
| 33 | |
| 34 | class MidiDriver_FluidSynth : public MidiDriver_Emulated { |
| 35 | private: |
| 36 | MidiChannel_MPU401 _midiChannels[16]; |
| 37 | fluid_settings_t *_settings; |
| 38 | fluid_synth_t *_synth; |
| 39 | int _soundFont; |
| 40 | int _outputRate; |
| 41 | SoundHandle _handle; |
| 42 | |
| 43 | protected: |
| 44 | // Because GCC complains about casting from const to non-const... |
| 45 | void setInt(const char *name, int val); |
| 46 | void setNum(const char *name, double num); |
| 47 | void setStr(const char *name, const char *str); |
| 48 | |
| 49 | void generateSamples(int16 *buf, int len); |
| 50 | |
| 51 | public: |
| 52 | MidiDriver_FluidSynth(SoundMixer *mixer); |
| 53 | |
| 54 | int open(); |
| 55 | void close(); |
| 56 | void send(uint32 b); |
| 57 | |
| 58 | MidiChannel *allocateChannel(); |
| 59 | MidiChannel *getPercussionChannel(); |
| 60 | |
| 61 | // AudioStream API |
| 62 | bool isStereo() const { return true; } |
| 63 | int getRate() const { return _outputRate; } |
| 64 | }; |
| 65 | |
| 66 | // MidiDriver method implementations |
| 67 | |
| 68 | MidiDriver_FluidSynth::MidiDriver_FluidSynth(SoundMixer *mixer) |
| 69 | : MidiDriver_Emulated(mixer) { |
| 70 | |
| 71 | for (int i = 0; i < ARRAYSIZE(_midiChannels); i++) { |
| 72 | _midiChannels[i].init(this, i); |
| 73 | } |
| 74 | |
| 75 | // It ought to be possible to get FluidSynth to generate samples at |
| 76 | // lower |
| 77 | |
| 78 | _outputRate = _mixer->getOutputRate(); |
| 79 | if (_outputRate < 22050) |
| 80 | _outputRate = 22050; |
| 81 | else if (_outputRate > 96000) |
| 82 | _outputRate = 96000; |
| 83 | } |
| 84 | |
| 85 | void MidiDriver_FluidSynth::setInt(const char *name, int val) { |
| 86 | char *name2 = strdup(name); |
| 87 | |
| 88 | fluid_settings_setint(_settings, name2, val); |
| 89 | free(name2); |
| 90 | } |
| 91 | |
| 92 | void MidiDriver_FluidSynth::setNum(const char *name, double val) { |
| 93 | char *name2 = strdup(name); |
| 94 | |
| 95 | fluid_settings_setnum(_settings, name2, val); |
| 96 | free(name2); |
| 97 | } |
| 98 | |
| 99 | void MidiDriver_FluidSynth::setStr(const char *name, const char *val) { |
| 100 | char *name2 = strdup(name); |
| 101 | char *val2 = strdup(val); |
| 102 | |
| 103 | fluid_settings_setstr(_settings, name2, val2); |
| 104 | free(name2); |
| 105 | free(val2); |
| 106 | } |
| 107 | |
| 108 | int MidiDriver_FluidSynth::open() { |
| 109 | if (_isOpen) |
| 110 | return MERR_ALREADY_OPEN; |
| 111 | |
| 112 | if (!ConfMan.hasKey("soundfont")) |
| 113 | error("FluidSynth recquires a 'soundfont' setting"); |
| 114 | |
| 115 | _settings = new_fluid_settings(); |
| 116 | |
| 117 | // The default gain setting is ridiculously low, but we can't set it |
| 118 | // too high either or sound will be clipped. This may need tuning... |
| 119 | |
| 120 | setNum("synth.gain", 2.1); |
| 121 | setNum("synth.sample-rate", _outputRate); |
| 122 | |
| 123 | _synth = new_fluid_synth(_settings); |
| 124 | |
| 125 | // In theory, this ought to reduce CPU load... but it doesn't make any |
| 126 | // noticeable difference for me, so disable it for now. |
| 127 | |
| 128 | // fluid_synth_set_interp_method(_synth, -1, FLUID_INTERP_LINEAR); |
| 129 | // fluid_synth_set_reverb_on(_synth, 0); |
| 130 | // fluid_synth_set_chorus_on(_synth, 0); |
| 131 | |
| 132 | const char *soundfont = ConfMan.get("soundfont").c_str(); |
| 133 | |
| 134 | _soundFont = fluid_synth_sfload(_synth, soundfont, 1); |
| 135 | if (_soundFont == -1) |
| 136 | error("Failed loading custom sound font '%s'", soundfont); |
| 137 | |
| 138 | MidiDriver_Emulated::open(); |
| 139 | |
| 140 | // The MT-32 emulator uses kSFXSoundType here. I don't know why. |
| 141 | _mixer->playInputStream(SoundMixer::kMusicSoundType, &_handle, this, -1, 255, 0, false, true); |
| 142 | return 0; |
| 143 | } |
| 144 | |
| 145 | void MidiDriver_FluidSynth::close() { |
| 146 | if (!_isOpen) |
| 147 | return; |
| 148 | _isOpen = false; |
| 149 | |
| 150 | _mixer->stopHandle(_handle); |
| 151 | |
| 152 | if (_soundFont != -1) |
| 153 | fluid_synth_sfunload(_synth, _soundFont, 1); |
| 154 | |
| 155 | delete_fluid_synth(_synth); |
| 156 | delete_fluid_settings(_settings); |
| 157 | } |
| 158 | |
| 159 | void MidiDriver_FluidSynth::send(uint32 b) { |
| 160 | //byte param3 = (byte) ((b >> 24) & 0xFF); |
| 161 | uint param2 = (byte) ((b >> 16) & 0xFF); |
| 162 | uint param1 = (byte) ((b >> 8) & 0xFF); |
| 163 | byte cmd = (byte) (b & 0xF0); |
| 164 | byte chan = (byte) (b & 0x0F); |
| 165 | |
| 166 | switch (cmd) { |
| 167 | case 0x80: // Note Off |
| 168 | fluid_synth_noteoff(_synth, chan, param1); |
| 169 | break; |
| 170 | case 0x90: // Note On |
| 171 | fluid_synth_noteon(_synth, chan, param1, param2); |
| 172 | break; |
| 173 | case 0xA0: // Aftertouch |
| 174 | break; |
| 175 | case 0xB0: // Control Change |
| 176 | fluid_synth_cc(_synth, chan, param1, param2); |
| 177 | break; |
| 178 | case 0xC0: // Program Change |
| 179 | fluid_synth_program_change(_synth, chan, param1); |
| 180 | break; |
| 181 | case 0xD0: // Channel Pressure |
| 182 | break; |
| 183 | case 0xE0: // Pitch Bend |
| 184 | fluid_synth_pitch_bend(_synth, chan, (param2 << 7) | param1); |
| 185 | break; |
| 186 | case 0xF0: // SysEx |
| 187 | // We should never get here! SysEx information has to be |
| 188 | // sent via high-level semantic methods. |
| 189 | warning("MidiDriver_FluidSynth: Receiving SysEx command on a send() call"); |
| 190 | break; |
| 191 | default: |
| 192 | warning("MidiDriver_FluidSynth: Unknown send() command 0x%02X", cmd); |
| 193 | break; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | MidiChannel *MidiDriver_FluidSynth::allocateChannel() { |
| 198 | for (int i = 0; i < ARRAYSIZE(_midiChannels); i++) { |
| 199 | if (i != 9 && _midiChannels[i].allocate()) |
| 200 | return &_midiChannels[i]; |
| 201 | } |
| 202 | return NULL; |
| 203 | } |
| 204 | |
| 205 | MidiChannel *MidiDriver_FluidSynth::getPercussionChannel() { |
| 206 | return &_midiChannels[9]; |
| 207 | } |
| 208 | |
| 209 | MidiDriver *MidiDriver_FluidSynth_create(SoundMixer *mixer) { |
| 210 | return new MidiDriver_FluidSynth(mixer); |
| 211 | } |
| 212 | |
| 213 | void MidiDriver_FluidSynth::generateSamples(int16 *data, int len) { |
| 214 | fluid_synth_write_s16(_synth, len, data, 0, 2, data, 1, 2); |
| 215 | } |
| 216 | |
| 217 | #endif |