1 /* 2 * Copyright 2006-2014, Haiku. 3 * 4 * Copyright (c) 2004-2005 Matthijs Hollemans 5 * Copyright (c) 2003 Jerome Leveque 6 * Distributed under the terms of the MIT License. 7 * 8 * Authors: 9 * Jérôme Duval 10 * Jérôme Leveque 11 * Matthijs Hollemans 12 * Pete Goodeve 13 */ 14 15 #include <MidiRoster.h> 16 #include <MidiConsumer.h> 17 #include <File.h> 18 #include <FindDirectory.h> 19 #include <Path.h> 20 #include <string.h> 21 #include <stdlib.h> 22 23 #include "debug.h" 24 #include "MidiGlue.h" // for MAKE_BIGTIME 25 #include "SoftSynth.h" 26 27 using namespace BPrivate; 28 29 struct ReverbSettings { 30 double room, damp, width, level; 31 } gReverbSettings[] = { 32 {0.0, 0.0, 0.0, 0.0}, // B_REVERB_NONE 33 {0.2, 0.0, 0.5, 0.9}, // B_REVERB_CLOSET 34 {0.5, 0.0, 0.9, 0.9}, // B_REVERB_GARAGE 35 {0.7, 0.25, 0.9, 0.95}, // B_REVERB_BALLROOM 36 {0.99, 0.3, 1.0, 1.0}, // B_REVERB_CAVERN 37 {1.03, 0.6, 1.0, 1.0} // B_REVERB_DUNGEON 38 }; 39 40 41 BSoftSynth::BSoftSynth() 42 : fInitCheck(false), 43 fSynth(NULL), 44 fSettings(NULL), 45 fSoundPlayer(NULL), 46 fMonitor(NULL), 47 fMonitorSize(0), 48 fMonitorChans(-1) 49 { 50 fInstrumentsFile = NULL; 51 SetDefaultInstrumentsFile(); 52 53 fSampleRate = 44100; 54 fInterpMode = B_LINEAR_INTERPOLATION; 55 fMaxVoices = 256; 56 fLimiterThreshold = 7; 57 fReverbEnabled = true; 58 fReverbMode = B_REVERB_BALLROOM; 59 } 60 61 62 BSoftSynth::~BSoftSynth() 63 { 64 // Note: it is possible that we don't get deleted. When BSynth is 65 // created, it is assigned to the global variable be_synth. While 66 // BSynth is alive, it keeps a copy of BSoftSynth around too. Not 67 // a big deal, but the Midi Kit will complain (on stdout) that we 68 // didn't release our endpoints. 69 70 delete[] fMonitor; 71 Unload(); 72 } 73 74 75 bool 76 BSoftSynth::InitCheck() 77 { 78 if (!fSynth) 79 _Init(); 80 return fInitCheck; 81 } 82 83 84 void 85 BSoftSynth::Unload(void) 86 { 87 _Done(); 88 free(fInstrumentsFile); 89 fInstrumentsFile = NULL; 90 } 91 92 93 bool 94 BSoftSynth::IsLoaded(void) const 95 { 96 return fInstrumentsFile != NULL; 97 } 98 99 100 status_t 101 BSoftSynth::SetDefaultInstrumentsFile() 102 { 103 // We first search for a setting file (or symlink to it) 104 // in the user settings directory 105 char buffer[512]; 106 BPath path; 107 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 108 path.Append("midi"); 109 BFile file(path.Path(), B_READ_ONLY); 110 if (file.InitCheck() == B_OK 111 && file.Read(buffer, sizeof(buffer)) > 0) { 112 char soundFont[512]; 113 sscanf(buffer, "# Midi Settings\n soundfont = %s\n", 114 soundFont); 115 return SetInstrumentsFile(soundFont); 116 } 117 } 118 119 // fall back to the old default 120 // softsynth (big_synth.sy) 121 // TODO: Remove this 122 if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) { 123 path.Append(B_BIG_SYNTH_FILE); 124 return SetInstrumentsFile(path.Path()); 125 } 126 127 // TODO: Use the first soundfont found in the synth directory 128 // instead of hardcoding 129 if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) { 130 path.Append("TimGM6mb.sf2"); 131 return SetInstrumentsFile(path.Path()); 132 } 133 134 // TODO: Write the settings file 135 136 return B_ERROR; 137 } 138 139 140 status_t 141 BSoftSynth::SetInstrumentsFile(const char* path) 142 { 143 if (path == NULL) 144 return B_BAD_VALUE; 145 146 if (!BEntry(path).Exists()) 147 return B_FILE_NOT_FOUND; 148 149 if (IsLoaded()) 150 Unload(); 151 152 fInstrumentsFile = strdup(path); 153 return B_OK; 154 } 155 156 157 status_t 158 BSoftSynth::LoadAllInstruments() 159 { 160 InitCheck(); 161 return B_OK; 162 } 163 164 165 status_t 166 BSoftSynth::LoadInstrument(int16 instrument) 167 { 168 UNIMPLEMENTED 169 return B_OK; 170 } 171 172 173 status_t 174 BSoftSynth::UnloadInstrument(int16 instrument) 175 { 176 UNIMPLEMENTED 177 return B_OK; 178 } 179 180 181 status_t 182 BSoftSynth::RemapInstrument(int16 from, int16 to) 183 { 184 UNIMPLEMENTED 185 return B_OK; 186 } 187 188 189 void 190 BSoftSynth::FlushInstrumentCache(bool startStopCache) 191 { 192 // TODO: we may decide not to support this function because it's weird! 193 194 UNIMPLEMENTED 195 } 196 197 198 void 199 BSoftSynth::SetVolume(double volume) 200 { 201 if (InitCheck()) 202 if (volume >= 0.0) { 203 fluid_synth_set_gain(fSynth, volume); 204 } 205 } 206 207 208 double 209 BSoftSynth::Volume(void) const 210 { 211 return fluid_synth_get_gain(fSynth); 212 } 213 214 215 status_t 216 BSoftSynth::SetSamplingRate(int32 rate) 217 { 218 if (rate == 22050 || rate == 44100 || rate == 48000) { 219 fSampleRate = rate; 220 return B_OK; 221 } 222 223 return B_BAD_VALUE; 224 } 225 226 227 int32 228 BSoftSynth::SamplingRate() const 229 { 230 return fSampleRate; 231 } 232 233 234 status_t 235 BSoftSynth::SetInterpolation(interpolation_mode mode) 236 { 237 // not used because our synth uses the same format than the soundplayer 238 fInterpMode = mode; 239 return B_OK; 240 } 241 242 243 interpolation_mode 244 BSoftSynth::Interpolation() const 245 { 246 return fInterpMode; 247 } 248 249 250 status_t 251 BSoftSynth::EnableReverb(bool enabled) 252 { 253 fReverbEnabled = enabled; 254 fluid_synth_set_reverb_on(fSynth, enabled); 255 return B_OK; 256 } 257 258 259 bool 260 BSoftSynth::IsReverbEnabled() const 261 { 262 return fReverbEnabled; 263 } 264 265 266 void 267 BSoftSynth::SetReverb(reverb_mode mode) 268 { 269 if (mode < B_REVERB_NONE || mode > B_REVERB_DUNGEON) 270 return; 271 272 fReverbMode = mode; 273 if (fSynth) { 274 // We access the table using "mode - 1" because B_REVERB_NONE == 1 275 ReverbSettings *rvb = &gReverbSettings[mode - 1]; 276 fluid_synth_set_reverb(fSynth, rvb->room, rvb->damp, rvb->width, 277 rvb->level); 278 } 279 } 280 281 282 reverb_mode 283 BSoftSynth::Reverb() const 284 { 285 return fReverbMode; 286 } 287 288 289 status_t 290 BSoftSynth::SetMaxVoices(int32 max) 291 { 292 if (max > 0 && max <= 4096) { 293 fMaxVoices = max; 294 return B_OK; 295 } 296 297 return B_BAD_VALUE; 298 } 299 300 301 int16 302 BSoftSynth::MaxVoices(void) const 303 { 304 return fMaxVoices; 305 } 306 307 308 status_t 309 BSoftSynth::SetLimiterThreshold(int32 threshold) 310 { 311 // not used 312 if (threshold > 0 && threshold <= 32) { 313 fLimiterThreshold = threshold; 314 return B_OK; 315 } 316 317 return B_BAD_VALUE; 318 } 319 320 321 int16 322 BSoftSynth::LimiterThreshold(void) const 323 { 324 return fLimiterThreshold; 325 } 326 327 328 void 329 BSoftSynth::Pause(void) 330 { 331 UNIMPLEMENTED 332 } 333 334 335 void 336 BSoftSynth::Resume(void) 337 { 338 UNIMPLEMENTED 339 } 340 341 342 void 343 BSoftSynth::NoteOff( 344 uchar channel, uchar note, uchar velocity, uint32 time) 345 { 346 if (InitCheck()) { 347 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 348 fluid_synth_noteoff(fSynth, channel - 1, note); // velocity isn't used in FS 349 } 350 } 351 352 353 void 354 BSoftSynth::NoteOn( 355 uchar channel, uchar note, uchar velocity, uint32 time) 356 { 357 if (InitCheck()) { 358 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 359 fluid_synth_noteon(fSynth, channel - 1, note, velocity); 360 } 361 } 362 363 364 void 365 BSoftSynth::KeyPressure( 366 uchar channel, uchar note, uchar pressure, uint32 time) 367 { 368 if (InitCheck()) { 369 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 370 // unavailable 371 } 372 } 373 374 375 void 376 BSoftSynth::ControlChange( 377 uchar channel, uchar controlNumber, uchar controlValue, uint32 time) 378 { 379 if (InitCheck()) { 380 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 381 fluid_synth_cc(fSynth, channel - 1, controlNumber, controlValue); 382 } 383 } 384 385 386 void 387 BSoftSynth::ProgramChange( 388 uchar channel, uchar programNumber, uint32 time) 389 { 390 if (InitCheck()) { 391 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 392 fluid_synth_program_change(fSynth, channel - 1, programNumber); 393 } 394 } 395 396 397 void 398 BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time) 399 { 400 if (InitCheck()) { 401 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 402 //unavailable 403 } 404 } 405 406 407 void 408 BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time) 409 { 410 if (InitCheck()) { 411 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 412 // fluid_synth only accepts an int 413 fluid_synth_pitch_bend(fSynth, channel - 1, 414 ((uint32)(msb & 0x7f) << 7) | (lsb & 0x7f)); 415 } 416 } 417 418 419 void 420 BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time) 421 { 422 if (InitCheck()) { 423 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 424 // unsupported 425 } 426 } 427 428 429 void 430 BSoftSynth::SystemCommon( 431 uchar status, uchar data1, uchar data2, uint32 time) 432 { 433 if (InitCheck()) { 434 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 435 // unsupported 436 } 437 } 438 439 440 void 441 BSoftSynth::SystemRealTime(uchar status, uint32 time) 442 { 443 if (InitCheck()) { 444 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 445 // unsupported 446 } 447 } 448 449 450 void 451 BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time) 452 { 453 if (InitCheck()) { 454 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 455 // unsupported 456 } 457 } 458 459 460 void 461 BSoftSynth::AllNotesOff(bool justChannel, uint32 time) 462 { 463 if (InitCheck()) { 464 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 465 466 // from BMidi::AllNotesOff 467 for (uchar channel = 1; channel <= 16; ++channel) { 468 fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0); 469 470 if (!justChannel) { 471 for (uchar note = 0; note <= 0x7F; ++note) { 472 fluid_synth_noteoff(fSynth, channel - 1, note); 473 } 474 } 475 } 476 } 477 } 478 479 480 void 481 BSoftSynth::_Init() 482 { 483 status_t err; 484 fInitCheck = false; 485 486 _Done(); 487 488 fSettings = new_fluid_settings(); 489 fluid_settings_setnum(fSettings, (char*)"synth.sample-rate", fSampleRate); 490 fluid_settings_setint(fSettings, (char*)"synth.polyphony", fMaxVoices); 491 492 fSynth = new_fluid_synth(fSettings); 493 if (!fSynth) 494 return; 495 496 err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1); 497 if (err < B_OK) { 498 fprintf(stderr, "error in fluid_synth_sfload\n"); 499 return; 500 } 501 502 SetReverb(fReverbMode); 503 504 media_raw_audio_format format = media_raw_audio_format::wildcard; 505 format.channel_count = 2; 506 format.frame_rate = fSampleRate; 507 format.format = media_raw_audio_format::B_AUDIO_FLOAT; 508 509 fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this); 510 err = fSoundPlayer->InitCheck(); 511 if (err != B_OK) { 512 fprintf(stderr, "error in BSoundPlayer\n"); 513 return; 514 } 515 516 fSoundPlayer->SetHasData(true); 517 fSoundPlayer->Start(); 518 519 fInitCheck = true; 520 } 521 522 523 void 524 BSoftSynth::_Done() 525 { 526 if (fSoundPlayer) { 527 fSoundPlayer->SetHasData(false); 528 fSoundPlayer->Stop(); 529 delete fSoundPlayer; 530 fSoundPlayer = NULL; 531 } 532 if (fSynth) { 533 delete_fluid_synth(fSynth); 534 fSynth = NULL; 535 } 536 if (fSettings) { 537 delete_fluid_settings(fSettings); 538 fSettings = NULL; 539 } 540 } 541 542 543 void 544 BSoftSynth::PlayBuffer(void* cookie, void* data, size_t size, 545 const media_raw_audio_format& format) 546 { 547 BSoftSynth* synth = (BSoftSynth*)cookie; 548 549 if (synth->fMonitorSize == 0) { 550 synth->fMonitor = (float*)new void*[size]; 551 synth->fMonitorSize = size; 552 synth->fMonitorChans = format.channel_count; 553 } 554 555 // we use float samples 556 if (synth->fSynth) { 557 fluid_synth_write_float( 558 synth->fSynth, size / sizeof(float) / format.channel_count, 559 data, 0, format.channel_count, 560 data, 1, format.channel_count); 561 562 memcpy(synth->fMonitor, data, size); 563 } 564 } 565 566