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