DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU Library General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #include <fluidsynth.h> 00020 #include <math.h> 00021 #include <string.h> 00022 #include "control.h" 00023 00024 /* Protect against multiple inclusions */ 00025 #ifndef MIXER_BUFSIZE 00026 #include "mixer.h" 00027 #endif 00028 00029 static MixerChannel *synthchan = NULL; 00030 static fluid_synth_t *synth_soft = NULL; 00031 static int synthsamplerate = 0; 00032 00033 static void synth_log(int level, 00034 #if !defined (FLUIDSYNTH_VERSION_MAJOR) || FLUIDSYNTH_VERSION_MAJOR >= 2 // Let version 2.x be the default 00035 const 00036 #endif 00037 char *message, 00038 void *data) { 00039 (void)data;//UNUSED 00040 00041 switch (level) { 00042 case FLUID_PANIC: 00043 case FLUID_ERR: 00044 LOG(LOG_ALL,LOG_ERROR)(message); 00045 break; 00046 00047 case FLUID_WARN: 00048 LOG(LOG_ALL,LOG_WARN)(message); 00049 break; 00050 00051 default: 00052 LOG(LOG_ALL,LOG_NORMAL)(message); 00053 break; 00054 } 00055 } 00056 00057 static void synth_CallBack(Bitu len) { 00058 if (synth_soft != NULL) { 00059 fluid_synth_write_s16(synth_soft, len, MixTemp, 0, 2, MixTemp, 1, 2); 00060 synthchan->AddSamples_s16(len,(Bit16s *)MixTemp); 00061 } 00062 } 00063 00064 #if defined (WIN32) || defined (OS2) 00065 # define PATH_SEP "\\" 00066 #else 00067 # define PATH_SEP "/" 00068 #endif 00069 00070 class MidiHandler_synth: public MidiHandler { 00071 private: 00072 fluid_settings_t *settings; 00073 int sfont_id; 00074 bool isOpen; 00075 00076 void PlayEvent(Bit8u *msg, Bitu len) { 00077 Bit8u event = msg[0], channel, p1, p2; 00078 00079 switch (event) { 00080 case 0xf0: 00081 case 0xf7: 00082 LOG(LOG_MISC,LOG_DEBUG)("SYNTH: sysex 0x%02x len %lu", (int)event, (long unsigned)len); 00083 fluid_synth_sysex(synth_soft, (char *)(msg + 1), len - 1, NULL, NULL, NULL, 0); 00084 return; 00085 case 0xf9: 00086 LOG(LOG_MISC,LOG_DEBUG)("SYNTH: midi tick"); 00087 return; 00088 case 0xff: 00089 LOG(LOG_MISC,LOG_DEBUG)("SYNTH: system reset"); 00090 fluid_synth_system_reset(synth_soft); 00091 return; 00092 case 0xf1: case 0xf2: case 0xf3: case 0xf4: 00093 case 0xf5: case 0xf6: case 0xf8: case 0xfa: 00094 case 0xfb: case 0xfc: case 0xfd: case 0xfe: 00095 LOG(LOG_MISC,LOG_WARN)("SYNTH: unhandled event 0x%02x", (int)event); 00096 return; 00097 } 00098 00099 channel = event & 0xf; 00100 p1 = len > 1 ? msg[1] : 0; 00101 p2 = len > 2 ? msg[2] : 0; 00102 00103 LOG(LOG_MISC,LOG_DEBUG)("SYNTH: event 0x%02x channel %d, 0x%02x 0x%02x", 00104 (int)event, (int)channel, (int)p1, (int)p2); 00105 00106 switch (event & 0xf0) { 00107 case 0x80: 00108 fluid_synth_noteoff(synth_soft, channel, p1); 00109 break; 00110 case 0x90: 00111 fluid_synth_noteon(synth_soft, channel, p1, p2); 00112 break; 00113 case 0xb0: 00114 fluid_synth_cc(synth_soft, channel, p1, p2); 00115 break; 00116 case 0xc0: 00117 fluid_synth_program_change(synth_soft, channel, p1); 00118 break; 00119 case 0xd0: 00120 fluid_synth_channel_pressure(synth_soft, channel, p1); 00121 break; 00122 case 0xe0: 00123 fluid_synth_pitch_bend(synth_soft, channel, (p2 << 7) | p1); 00124 break; 00125 } 00126 }; 00127 00128 public: 00129 MidiHandler_synth() : MidiHandler(),isOpen(false) {}; 00130 00131 const char * GetName(void) { 00132 return "synth"; 00133 }; 00134 00135 bool Open(const char *conf) { 00136 if (isOpen) return false; 00137 00138 /* Sound font file required */ 00139 if (!conf || (conf[0] == '\0')) { 00140 LOG(LOG_MISC,LOG_DEBUG)("SYNTH: Specify .SF2 sound font file with midiconfig="); 00141 return false; 00142 } 00143 00144 fluid_set_log_function(FLUID_PANIC, synth_log, NULL); 00145 fluid_set_log_function(FLUID_ERR, synth_log, NULL); 00146 fluid_set_log_function(FLUID_WARN, synth_log, NULL); 00147 fluid_set_log_function(FLUID_INFO, synth_log, NULL); 00148 fluid_set_log_function(FLUID_DBG, synth_log, NULL); 00149 00150 /* Create the settings. */ 00151 settings = new_fluid_settings(); 00152 if (settings == NULL) { 00153 LOG(LOG_MISC,LOG_WARN)("SYNTH: Error allocating MIDI soft synth settings"); 00154 return false; 00155 } 00156 00157 fluid_settings_setstr(settings, "audio.sample-format", "16bits"); 00158 fluid_settings_setnum(settings, "synth.sample-rate", (double)synthsamplerate); 00159 //fluid_settings_setnum(settings, "synth.gain", 0.5); 00160 00161 /* Create the synthesizer. */ 00162 synth_soft = new_fluid_synth(settings); 00163 if (synth_soft == NULL) { 00164 LOG(LOG_MISC,LOG_WARN)("SYNTH: Error initialising MIDI soft synth"); 00165 delete_fluid_settings(settings); 00166 return false; 00167 } 00168 00169 /* Load a SoundFont */ 00170 sfont_id = fluid_synth_sfload(synth_soft, conf, 0); 00171 if (sfont_id == -1) { 00172 extern std::string capturedir; 00173 std::string str = capturedir + std::string(PATH_SEP) + std::string(conf); 00174 sfont_id = fluid_synth_sfload(synth_soft, str.c_str(), 0); 00175 } 00176 00177 if (sfont_id == -1) { 00178 LOG(LOG_MISC,LOG_WARN)("SYNTH: Failed to load MIDI sound font file \"%s\"", 00179 conf); 00180 delete_fluid_synth(synth_soft); 00181 delete_fluid_settings(settings); 00182 return false; 00183 } 00184 00185 synthchan = MIXER_AddChannel(synth_CallBack, (unsigned int)synthsamplerate, "SYNTH"); 00186 synthchan->Enable(false); 00187 isOpen = true; 00188 return true; 00189 }; 00190 00191 void Close(void) { 00192 if (!isOpen) return; 00193 00194 synthchan->Enable(false); 00195 MIXER_DelChannel(synthchan); 00196 delete_fluid_synth(synth_soft); 00197 delete_fluid_settings(settings); 00198 00199 synthchan = NULL; 00200 synth_soft = NULL; 00201 settings = NULL; 00202 isOpen = false; 00203 }; 00204 00205 void PlayMsg(Bit8u *msg) { 00206 synthchan->Enable(true); 00207 PlayEvent(msg, MIDI_evt_len[*msg]); 00208 }; 00209 00210 void PlaySysex(Bit8u *sysex, Bitu len) { 00211 PlayEvent(sysex, len); 00212 }; 00213 }; 00214 00215 MidiHandler_synth Midi_synth; 00216 00217 class MidiHandler_fluidsynth : public MidiHandler { 00218 private: 00219 std::string soundfont; 00220 int soundfont_id; 00221 fluid_settings_t *settings; 00222 fluid_synth_t *synth; 00223 fluid_audio_driver_t* adriver; 00224 public: 00225 MidiHandler_fluidsynth() : MidiHandler() {}; 00226 const char* GetName(void) { return "fluidsynth"; } 00227 void PlaySysex(Bit8u * sysex, Bitu len) { 00228 fluid_synth_sysex(synth, (char*)sysex, len, NULL, NULL, NULL, 0); 00229 } 00230 00231 void PlayMsg(Bit8u * msg) { 00232 unsigned char chanID = msg[0] & 0x0F; 00233 switch (msg[0] & 0xF0) { 00234 case 0x80: 00235 fluid_synth_noteoff(synth, chanID, msg[1]); 00236 break; 00237 case 0x90: 00238 fluid_synth_noteon(synth, chanID, msg[1], msg[2]); 00239 break; 00240 case 0xB0: 00241 fluid_synth_cc(synth, chanID, msg[1], msg[2]); 00242 break; 00243 case 0xC0: 00244 fluid_synth_program_change(synth, chanID, msg[1]); 00245 break; 00246 case 0xD0: 00247 fluid_synth_channel_pressure(synth, chanID, msg[1]); 00248 break; 00249 case 0xE0: { 00250 long theBend = ((long)msg[1] + (long)(msg[2] << 7)); 00251 fluid_synth_pitch_bend(synth, chanID, theBend); 00252 } 00253 break; 00254 default: 00255 LOG(LOG_MISC, LOG_WARN)("MIDI:fluidsynth: Unknown Command: %08lx", (long)msg[0]); 00256 break; 00257 } 00258 } 00259 00260 void Close(void) { 00261 if (soundfont_id >= 0) { 00262 fluid_synth_sfunload(synth, soundfont_id, 0); 00263 } 00264 delete_fluid_audio_driver(adriver); 00265 delete_fluid_synth(synth); 00266 delete_fluid_settings(settings); 00267 } 00268 00269 bool Open(const char * conf) { 00270 (void)conf; 00271 00272 Section_prop *section = static_cast<Section_prop *>(control->GetSection("midi")); 00273 const char *sf = section->Get_string("fluid.soundfont"); 00274 if (!*sf) { // Let's try to find a soundfont before bailing 00275 #if defined (WIN32) 00276 // default for windows according to fluidsynth docs 00277 if (FILE *file = fopen("C:\\soundfonts\\default.sf2", "r")) { 00278 fclose(file); 00279 sf = "C:\\soundfonts\\default.sf2"; 00280 } else { 00281 LOG_MSG("MIDI:fluidsynth: SoundFont not specified"); 00282 return false; 00283 } 00284 #else 00285 // Default on "other" platforms according to fluidsynth docs 00286 // This works on RH and Fedora, if a soundfont is installed 00287 if (FILE *file = fopen("/usr/share/soundfonts/default.sf2", "r")) { 00288 fclose(file); 00289 sf = "/usr/share/soundfonts/default.sf2"; 00290 // Ubuntu and Debian don't have a default.sf2... 00291 } else if (FILE *file = fopen("/usr/share/sounds/sf2/FluidR3_GM.sf2", "r")) { 00292 fclose(file); 00293 sf = "/usr/share/sounds/sf2/FluidR3_GM.sf2"; 00294 } else { 00295 LOG_MSG("MIDI:fluidsynth: SoundFont not specified, and no system SoundFont found"); 00296 return false; 00297 } 00298 #endif 00299 } 00300 soundfont.assign(sf); 00301 settings = new_fluid_settings(); 00302 if (strcmp(section->Get_string("fluid.driver"), "default") != 0) { 00303 fluid_settings_setstr(settings, "audio.driver", section->Get_string("fluid.driver")); 00304 } 00305 #if defined (__linux__) // Let's use pulseaudio as default on Linux, and not the FluidSynth default of Jack 00306 else 00307 fluid_settings_setstr(settings, "audio.driver", "pulseaudio"); 00308 #endif 00309 fluid_settings_setnum(settings, "synth.sample-rate", atof(section->Get_string("fluid.samplerate"))); 00310 fluid_settings_setnum(settings, "synth.gain", atof(section->Get_string("fluid.gain"))); 00311 fluid_settings_setint(settings, "synth.polyphony", section->Get_int("fluid.polyphony")); 00312 if (strcmp(section->Get_string("fluid.cores"), "default") != 0) { 00313 fluid_settings_setnum(settings, "synth.cpu-cores", atof(section->Get_string("fluid.cores"))); 00314 } 00315 00316 std::string period=section->Get_string("fluid.periods"), periodsize=section->Get_string("fluid.periodsize"); 00317 if (period=="default") { 00318 #if defined (WIN32) 00319 period="8"; 00320 #else 00321 period="16"; 00322 #endif 00323 } 00324 if (periodsize=="default") { 00325 #if defined (WIN32) 00326 periodsize="512"; 00327 #else 00328 periodsize="64"; 00329 #endif 00330 } 00331 #if !defined (FLUIDSYNTH_VERSION_MAJOR) || FLUIDSYNTH_VERSION_MAJOR >= 2 00332 fluid_settings_setint(settings, "audio.periods", atoi(period.c_str())); 00333 fluid_settings_setint(settings, "audio.period-size", atoi(periodsize.c_str())); 00334 fluid_settings_setint(settings, "synth.reverb.active", !strcmp(section->Get_string("fluid.reverb"), "yes")?1:0); 00335 fluid_settings_setint(settings, "synth.chorus.active", !strcmp(section->Get_string("fluid.chorus"), "yes")?1:0); 00336 #else 00337 fluid_settings_setnum(settings, "audio.periods", atof(period.c_str())); 00338 fluid_settings_setnum(settings, "audio.period-size", atof(periodsize.c_str())); 00339 fluid_settings_setstr(settings, "synth.reverb.active", section->Get_string("fluid.reverb")); 00340 fluid_settings_setstr(settings, "synth.chorus.active", section->Get_string("fluid.chorus")); 00341 #endif 00342 00343 synth = new_fluid_synth(settings); 00344 if (!synth) { 00345 LOG_MSG("MIDI:fluidsynth: Can't open synthesiser"); 00346 delete_fluid_settings(settings); 00347 return false; 00348 } 00349 00350 adriver = new_fluid_audio_driver(settings, synth); 00351 if (!adriver) { 00352 LOG_MSG("MIDI:fluidsynth: Can't create audio driver"); 00353 delete_fluid_synth(synth); 00354 delete_fluid_settings(settings); 00355 return false; 00356 } 00357 00358 fluid_synth_set_reverb(synth, atof(section->Get_string("fluid.reverb.roomsize")), atof(section->Get_string("fluid.reverb.damping")), atof(section->Get_string("fluid.reverb.width")), atof(section->Get_string("fluid.reverb.level"))); 00359 00360 fluid_synth_set_chorus(synth, section->Get_int("fluid.chorus.number"), atof(section->Get_string("fluid.chorus.level")), atof(section->Get_string("fluid.chorus.speed")), atof(section->Get_string("fluid.chorus.depth")), section->Get_int("fluid.chorus.type")); 00361 00362 /* Optionally load a soundfont */ 00363 if (!soundfont.empty()) { 00364 soundfont_id = fluid_synth_sfload(synth, soundfont.c_str(), 1); 00365 if (soundfont_id == FLUID_FAILED) { 00366 /* Just consider this a warning (fluidsynth already prints) */ 00367 soundfont.clear(); 00368 soundfont_id = -1; 00369 } 00370 else { 00371 LOG_MSG("MIDI:fluidsynth: Loaded SoundFont: %s", soundfont.c_str()); 00372 } 00373 } 00374 else { 00375 soundfont_id = -1; 00376 LOG_MSG("MIDI:fluidsynth: No SoundFont loaded"); 00377 } 00378 return true; 00379 } 00380 }; 00381 00382 MidiHandler_fluidsynth Midi_fluidsynth;