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, ((uint32)(msb & 0x7f) << 8) | (lsb & 0x7f)); 360 } 361 } 362 363 364 void 365 BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time) 366 { 367 if (InitCheck()) { 368 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 369 // unsupported 370 } 371 } 372 373 374 void 375 BSoftSynth::SystemCommon( 376 uchar status, uchar data1, uchar data2, uint32 time) 377 { 378 if (InitCheck()) { 379 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 380 // unsupported 381 } 382 } 383 384 385 void 386 BSoftSynth::SystemRealTime(uchar status, uint32 time) 387 { 388 if (InitCheck()) { 389 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 390 // unsupported 391 } 392 } 393 394 395 void 396 BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time) 397 { 398 if (InitCheck()) { 399 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 400 // unsupported 401 } 402 } 403 404 405 void 406 BSoftSynth::AllNotesOff(bool justChannel, uint32 time) 407 { 408 if (InitCheck()) { 409 snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); 410 411 // from BMidi::AllNotesOff 412 for (uchar channel = 1; channel <= 16; ++channel) { 413 fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0); 414 415 if (!justChannel) { 416 for (uchar note = 0; note <= 0x7F; ++note) { 417 fluid_synth_noteoff(fSynth, channel - 1, note); 418 } 419 } 420 } 421 } 422 } 423 424 425 void 426 BSoftSynth::_Init() 427 { 428 status_t err; 429 fInitCheck = false; 430 431 _Done(); 432 433 fSettings = new_fluid_settings(); 434 fluid_settings_setnum(fSettings, "synth.sample-rate", fSampleRate); 435 fluid_settings_setint(fSettings, "synth.polyphony", fMaxVoices); 436 437 fSynth = new_fluid_synth(fSettings); 438 if (!fSynth) 439 return; 440 441 err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1); 442 if (err < B_OK) { 443 fprintf(stderr, "error in fluid_synth_sfload\n"); 444 return; 445 } 446 447 media_raw_audio_format format = media_raw_audio_format::wildcard; 448 format.channel_count = 2; 449 format.frame_rate = fSampleRate; 450 format.format = media_raw_audio_format::B_AUDIO_FLOAT; 451 452 fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this); 453 err = fSoundPlayer->InitCheck(); 454 if (err != B_OK) { 455 fprintf(stderr, "error in BSoundPlayer\n"); 456 return; 457 } 458 459 fSoundPlayer->SetHasData(true); 460 fSoundPlayer->Start(); 461 462 fInitCheck = true; 463 } 464 465 466 void 467 BSoftSynth::_Done() 468 { 469 if (fSoundPlayer) { 470 fSoundPlayer->SetHasData(false); 471 fSoundPlayer->Stop(); 472 delete fSoundPlayer; 473 fSoundPlayer = NULL; 474 } 475 if (fSynth) { 476 delete_fluid_synth(fSynth); 477 fSynth = NULL; 478 } 479 if (fSettings) { 480 delete_fluid_settings(fSettings); 481 fSettings = NULL; 482 } 483 } 484 485 486 void 487 BSoftSynth::PlayBuffer(void * cookie, void * data, size_t size, const media_raw_audio_format & format) 488 { 489 BSoftSynth *synth = (BSoftSynth *)cookie; 490 491 // we use float samples 492 493 if (synth->fSynth) 494 fluid_synth_write_float(synth->fSynth, size / sizeof(float) / format.channel_count, 495 data, 0, format.channel_count, 496 data, 1, format.channel_count); 497 } 498 499