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 // TODO: Use the first soundfont found in the synth directory 120 // instead of hardcoding 121 if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) { 122 path.Append("TimGM6mb.sf2"); 123 return SetInstrumentsFile(path.Path()); 124 } 125 126 // TODO: Write the settings file 127 128 return B_ERROR; 129 } 130 131 132 status_t 133 BSoftSynth::SetInstrumentsFile(const char* path) 134 { 135 if (path == NULL) 136 return B_BAD_VALUE; 137 138 if (!BEntry(path).Exists()) 139 return B_FILE_NOT_FOUND; 140 141 if (IsLoaded()) 142 Unload(); 143 144 fInstrumentsFile = strdup(path); 145 return B_OK; 146 } 147 148 149 status_t 150 BSoftSynth::LoadAllInstruments() 151 { 152 InitCheck(); 153 return B_OK; 154 } 155 156 157 status_t 158 BSoftSynth::LoadInstrument(int16 instrument) 159 { 160 UNIMPLEMENTED 161 return B_OK; 162 } 163 164 165 status_t 166 BSoftSynth::UnloadInstrument(int16 instrument) 167 { 168 UNIMPLEMENTED 169 return B_OK; 170 } 171 172 173 status_t 174 BSoftSynth::RemapInstrument(int16 from, int16 to) 175 { 176 UNIMPLEMENTED 177 return B_OK; 178 } 179 180 181 void 182 BSoftSynth::FlushInstrumentCache(bool startStopCache) 183 { 184 // TODO: we may decide not to support this function because it's weird! 185 186 UNIMPLEMENTED 187 } 188 189 190 void 191 BSoftSynth::SetVolume(double volume) 192 { 193 if (InitCheck()) 194 if (volume >= 0.0) { 195 fluid_synth_set_gain(fSynth, volume); 196 } 197 } 198 199 200 double 201 BSoftSynth::Volume(void) const 202 { 203 return fluid_synth_get_gain(fSynth); 204 } 205 206 207 status_t 208 BSoftSynth::SetSamplingRate(int32 rate) 209 { 210 if (rate == 22050 || rate == 44100 || rate == 48000) { 211 fSampleRate = rate; 212 return B_OK; 213 } 214 215 return B_BAD_VALUE; 216 } 217 218 219 int32 220 BSoftSynth::SamplingRate() const 221 { 222 return fSampleRate; 223 } 224 225 226 status_t 227 BSoftSynth::SetInterpolation(interpolation_mode mode) 228 { 229 // not used because our synth uses the same format than the soundplayer 230 fInterpMode = mode; 231 return B_OK; 232 } 233 234 235 interpolation_mode 236 BSoftSynth::Interpolation() const 237 { 238 return fInterpMode; 239 } 240 241 242 status_t 243 BSoftSynth::EnableReverb(bool enabled) 244 { 245 fReverbEnabled = enabled; 246 fluid_synth_set_reverb_on(fSynth, enabled); 247 return B_OK; 248 } 249 250 251 bool 252 BSoftSynth::IsReverbEnabled() const 253 { 254 return fReverbEnabled; 255 } 256 257 258 void 259 BSoftSynth::SetReverb(reverb_mode mode) 260 { 261 if (mode < B_REVERB_NONE || mode > B_REVERB_DUNGEON) 262 return; 263 264 fReverbMode = mode; 265 if (fSynth) { 266 // We access the table using "mode - 1" because B_REVERB_NONE == 1 267 ReverbSettings *rvb = &gReverbSettings[mode - 1]; 268 fluid_synth_set_reverb(fSynth, rvb->room, rvb->damp, rvb->width, 269 rvb->level); 270 } 271 } 272 273 274 reverb_mode 275 BSoftSynth::Reverb() const 276 { 277 return fReverbMode; 278 } 279 280 281 status_t 282 BSoftSynth::SetMaxVoices(int32 max) 283 { 284 if (max > 0 && max <= 4096) { 285 fMaxVoices = max; 286 return B_OK; 287 } 288 289 return B_BAD_VALUE; 290 } 291 292 293 int16 294 BSoftSynth::MaxVoices(void) const 295 { 296 return fMaxVoices; 297 } 298 299 300 status_t 301 BSoftSynth::SetLimiterThreshold(int32 threshold) 302 { 303 // not used 304 if (threshold > 0 && threshold <= 32) { 305 fLimiterThreshold = threshold; 306 return B_OK; 307 } 308 309 return B_BAD_VALUE; 310 } 311 312 313 int16 314 BSoftSynth::LimiterThreshold(void) const 315 { 316 return fLimiterThreshold; 317 } 318 319 320 void 321 BSoftSynth::Pause(void) 322 { 323 UNIMPLEMENTED 324 } 325 326 327 void 328 BSoftSynth::Resume(void) 329 { 330 UNIMPLEMENTED 331 } 332 333 334 void 335 BSoftSynth::NoteOff( 336 uchar channel, uchar note, uchar velocity, uint32 time) 337 { 338 if (InitCheck()) { 339 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 340 fluid_synth_noteoff(fSynth, channel - 1, note); // velocity isn't used in FS 341 } 342 } 343 344 345 void 346 BSoftSynth::NoteOn( 347 uchar channel, uchar note, uchar velocity, uint32 time) 348 { 349 if (InitCheck()) { 350 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 351 fluid_synth_noteon(fSynth, channel - 1, note, velocity); 352 } 353 } 354 355 356 void 357 BSoftSynth::KeyPressure( 358 uchar channel, uchar note, uchar pressure, uint32 time) 359 { 360 if (InitCheck()) { 361 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 362 // unavailable 363 } 364 } 365 366 367 void 368 BSoftSynth::ControlChange( 369 uchar channel, uchar controlNumber, uchar controlValue, uint32 time) 370 { 371 if (InitCheck()) { 372 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 373 fluid_synth_cc(fSynth, channel - 1, controlNumber, controlValue); 374 } 375 } 376 377 378 void 379 BSoftSynth::ProgramChange( 380 uchar channel, uchar programNumber, uint32 time) 381 { 382 if (InitCheck()) { 383 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 384 fluid_synth_program_change(fSynth, channel - 1, programNumber); 385 } 386 } 387 388 389 void 390 BSoftSynth::ChannelPressure(uchar channel, 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::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time) 401 { 402 if (InitCheck()) { 403 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 404 // fluid_synth only accepts an int 405 fluid_synth_pitch_bend(fSynth, channel - 1, 406 ((uint32)(msb & 0x7f) << 7) | (lsb & 0x7f)); 407 } 408 } 409 410 411 void 412 BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time) 413 { 414 if (InitCheck()) { 415 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 416 // unsupported 417 } 418 } 419 420 421 void 422 BSoftSynth::SystemCommon( 423 uchar status, uchar data1, uchar data2, uint32 time) 424 { 425 if (InitCheck()) { 426 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 427 // unsupported 428 } 429 } 430 431 432 void 433 BSoftSynth::SystemRealTime(uchar status, uint32 time) 434 { 435 if (InitCheck()) { 436 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 437 // unsupported 438 } 439 } 440 441 442 void 443 BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time) 444 { 445 if (InitCheck()) { 446 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 447 // unsupported 448 } 449 } 450 451 452 void 453 BSoftSynth::AllNotesOff(bool justChannel, uint32 time) 454 { 455 if (InitCheck()) { 456 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 457 458 // from BMidi::AllNotesOff 459 for (uchar channel = 1; channel <= 16; ++channel) { 460 fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0); 461 462 if (!justChannel) { 463 for (uchar note = 0; note <= 0x7F; ++note) { 464 fluid_synth_noteoff(fSynth, channel - 1, note); 465 } 466 } 467 } 468 } 469 } 470 471 472 void 473 BSoftSynth::_Init() 474 { 475 status_t err; 476 fInitCheck = false; 477 478 _Done(); 479 480 fSettings = new_fluid_settings(); 481 fluid_settings_setnum(fSettings, (char*)"synth.sample-rate", fSampleRate); 482 fluid_settings_setint(fSettings, (char*)"synth.polyphony", fMaxVoices); 483 484 fSynth = new_fluid_synth(fSettings); 485 if (!fSynth) 486 return; 487 488 err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1); 489 if (err < B_OK) { 490 fprintf(stderr, "error in fluid_synth_sfload\n"); 491 return; 492 } 493 494 SetReverb(fReverbMode); 495 496 media_raw_audio_format format = media_raw_audio_format::wildcard; 497 format.channel_count = 2; 498 format.frame_rate = fSampleRate; 499 format.format = media_raw_audio_format::B_AUDIO_FLOAT; 500 501 fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this); 502 err = fSoundPlayer->InitCheck(); 503 if (err != B_OK) { 504 fprintf(stderr, "error in BSoundPlayer\n"); 505 return; 506 } 507 508 fSoundPlayer->SetHasData(true); 509 fSoundPlayer->Start(); 510 511 fInitCheck = true; 512 } 513 514 515 void 516 BSoftSynth::_Done() 517 { 518 if (fSoundPlayer) { 519 fSoundPlayer->SetHasData(false); 520 fSoundPlayer->Stop(); 521 delete fSoundPlayer; 522 fSoundPlayer = NULL; 523 } 524 if (fSynth) { 525 delete_fluid_synth(fSynth); 526 fSynth = NULL; 527 } 528 if (fSettings) { 529 delete_fluid_settings(fSettings); 530 fSettings = NULL; 531 } 532 } 533 534 535 void 536 BSoftSynth::PlayBuffer(void* cookie, void* data, size_t size, 537 const media_raw_audio_format& format) 538 { 539 BSoftSynth* synth = (BSoftSynth*)cookie; 540 541 if (synth->fMonitorSize == 0) { 542 synth->fMonitor = (float*)new void*[size]; 543 synth->fMonitorSize = size; 544 synth->fMonitorChans = format.channel_count; 545 } 546 547 // we use float samples 548 if (synth->fSynth) { 549 fluid_synth_write_float( 550 synth->fSynth, size / sizeof(float) / format.channel_count, 551 data, 0, format.channel_count, 552 data, 1, format.channel_count); 553 554 memcpy(synth->fMonitor, data, size); 555 } 556 } 557 558