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 34using namespace BPrivate; 35 36struct 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 48BSoftSynth::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 69BSoftSynth::~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 82bool 83BSoftSynth::InitCheck() 84{ 85 if (!fSynth) 86 _Init(); 87 return fInitCheck; 88} 89 90 91void 92BSoftSynth::Unload(void) 93{ 94 _Done(); 95 free(fInstrumentsFile); 96 fInstrumentsFile = NULL; 97} 98 99 100bool 101BSoftSynth::IsLoaded(void) const 102{ 103 return fInstrumentsFile != NULL; 104} 105 106 107status_t 108BSoftSynth::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 164status_t 165BSoftSynth::SetInstrumentsFile(const char* path) 166{ 167 if (path == NULL) 168 return B_BAD_VALUE; 169 170 if (!BEntry(path).Exists()) 171 return B_ENTRY_NOT_FOUND; 172 173 if (IsLoaded()) 174 Unload(); 175 176 fInstrumentsFile = strdup(path); 177 return B_OK; 178} 179 180 181status_t 182BSoftSynth::LoadAllInstruments() 183{ 184 InitCheck(); 185 return B_OK; 186} 187 188 189status_t 190BSoftSynth::LoadInstrument(int16 instrument) 191{ 192 UNIMPLEMENTED 193 return B_OK; 194} 195 196 197status_t 198BSoftSynth::UnloadInstrument(int16 instrument) 199{ 200 UNIMPLEMENTED 201 return B_OK; 202} 203 204 205status_t 206BSoftSynth::RemapInstrument(int16 from, int16 to) 207{ 208 UNIMPLEMENTED 209 return B_OK; 210} 211 212 213void 214BSoftSynth::FlushInstrumentCache(bool startStopCache) 215{ 216 // TODO: we may decide not to support this function because it's weird! 217 218 UNIMPLEMENTED 219} 220 221 222void 223BSoftSynth::SetVolume(double volume) 224{ 225 if (InitCheck()) 226 if (volume >= 0.0) { 227 fluid_synth_set_gain(fSynth, volume); 228 } 229} 230 231 232double 233BSoftSynth::Volume(void) const 234{ 235 return fluid_synth_get_gain(fSynth); 236} 237 238 239status_t 240BSoftSynth::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 251int32 252BSoftSynth::SamplingRate() const 253{ 254 return fSampleRate; 255} 256 257 258status_t 259BSoftSynth::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 267interpolation_mode 268BSoftSynth::Interpolation() const 269{ 270 return fInterpMode; 271} 272 273 274status_t 275BSoftSynth::EnableReverb(bool enabled) 276{ 277 fReverbEnabled = enabled; 278 fluid_synth_set_reverb_on(fSynth, enabled); 279 return B_OK; 280} 281 282 283bool 284BSoftSynth::IsReverbEnabled() const 285{ 286 return fReverbEnabled; 287} 288 289 290void 291BSoftSynth::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 306reverb_mode 307BSoftSynth::Reverb() const 308{ 309 return fReverbMode; 310} 311 312 313status_t 314BSoftSynth::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 325int16 326BSoftSynth::MaxVoices(void) const 327{ 328 return fMaxVoices; 329} 330 331 332status_t 333BSoftSynth::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 345int16 346BSoftSynth::LimiterThreshold(void) const 347{ 348 return fLimiterThreshold; 349} 350 351 352void 353BSoftSynth::Pause(void) 354{ 355 UNIMPLEMENTED 356} 357 358 359void 360BSoftSynth::Resume(void) 361{ 362 UNIMPLEMENTED 363} 364 365 366void 367BSoftSynth::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 377void 378BSoftSynth::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 388void 389BSoftSynth::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 399void 400BSoftSynth::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 410void 411BSoftSynth::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 421void 422BSoftSynth::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 431void 432BSoftSynth::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 443void 444BSoftSynth::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 453void 454BSoftSynth::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 464void 465BSoftSynth::SystemRealTime(uchar status, uint32 time) 466{ 467 if (InitCheck()) { 468 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 469 // unsupported 470 } 471} 472 473 474void 475BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time) 476{ 477 if (InitCheck()) { 478 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 479 // unsupported 480 } 481} 482 483 484void 485BSoftSynth::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 504void 505BSoftSynth::_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 547void 548BSoftSynth::_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 567void 568BSoftSynth::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