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