2 * Copyright 2006-2014, Haiku.
4 * Copyright (c) 2004-2005 Matthijs Hollemans
5 * Copyright (c) 2003 Jerome Leveque
6 * Distributed under the terms of the MIT License.
15 #include <MidiRoster.h>
16 #include <MidiConsumer.h>
18 #include <FindDirectory.h>
24 #include "MidiGlue.h" // for MAKE_BIGTIME
25 #include "SoftSynth.h"
27 using namespace BPrivate
;
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
41 BSoftSynth::BSoftSynth()
50 fInstrumentsFile
= NULL
;
51 SetDefaultInstrumentsFile();
54 fInterpMode
= B_LINEAR_INTERPOLATION
;
56 fLimiterThreshold
= 7;
57 fReverbEnabled
= true;
58 fReverbMode
= B_REVERB_BALLROOM
;
62 BSoftSynth::~BSoftSynth()
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.
76 BSoftSynth::InitCheck()
85 BSoftSynth::Unload(void)
88 free(fInstrumentsFile
);
89 fInstrumentsFile
= NULL
;
94 BSoftSynth::IsLoaded(void) const
96 return fInstrumentsFile
!= NULL
;
101 BSoftSynth::SetDefaultInstrumentsFile()
103 // We first search for a setting file (or symlink to it)
104 // in the user settings directory
107 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) == B_OK
) {
109 BFile
file(path
.Path(), B_READ_ONLY
);
110 if (file
.InitCheck() == B_OK
111 && file
.Read(buffer
, sizeof(buffer
)) > 0) {
113 sscanf(buffer
, "# Midi Settings\n soundfont = %s\n",
115 return SetInstrumentsFile(soundFont
);
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());
126 // TODO: Write the settings file
133 BSoftSynth::SetInstrumentsFile(const char* path
)
138 if (!BEntry(path
).Exists())
139 return B_FILE_NOT_FOUND
;
144 fInstrumentsFile
= strdup(path
);
150 BSoftSynth::LoadAllInstruments()
158 BSoftSynth::LoadInstrument(int16 instrument
)
166 BSoftSynth::UnloadInstrument(int16 instrument
)
174 BSoftSynth::RemapInstrument(int16 from
, int16 to
)
182 BSoftSynth::FlushInstrumentCache(bool startStopCache
)
184 // TODO: we may decide not to support this function because it's weird!
191 BSoftSynth::SetVolume(double volume
)
195 fluid_synth_set_gain(fSynth
, volume
);
201 BSoftSynth::Volume(void) const
203 return fluid_synth_get_gain(fSynth
);
208 BSoftSynth::SetSamplingRate(int32 rate
)
210 if (rate
== 22050 || rate
== 44100 || rate
== 48000) {
220 BSoftSynth::SamplingRate() const
227 BSoftSynth::SetInterpolation(interpolation_mode mode
)
229 // not used because our synth uses the same format than the soundplayer
236 BSoftSynth::Interpolation() const
243 BSoftSynth::EnableReverb(bool enabled
)
245 fReverbEnabled
= enabled
;
246 fluid_synth_set_reverb_on(fSynth
, enabled
);
252 BSoftSynth::IsReverbEnabled() const
254 return fReverbEnabled
;
259 BSoftSynth::SetReverb(reverb_mode mode
)
261 if (mode
< B_REVERB_NONE
|| mode
> B_REVERB_DUNGEON
)
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
,
275 BSoftSynth::Reverb() const
282 BSoftSynth::SetMaxVoices(int32 max
)
284 if (max
> 0 && max
<= 4096) {
294 BSoftSynth::MaxVoices(void) const
301 BSoftSynth::SetLimiterThreshold(int32 threshold
)
304 if (threshold
> 0 && threshold
<= 32) {
305 fLimiterThreshold
= threshold
;
314 BSoftSynth::LimiterThreshold(void) const
316 return fLimiterThreshold
;
321 BSoftSynth::Pause(void)
328 BSoftSynth::Resume(void)
336 uchar channel
, uchar note
, uchar velocity
, uint32 time
)
339 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
340 fluid_synth_noteoff(fSynth
, channel
- 1, note
); // velocity isn't used in FS
347 uchar channel
, uchar note
, uchar velocity
, uint32 time
)
350 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
351 fluid_synth_noteon(fSynth
, channel
- 1, note
, velocity
);
357 BSoftSynth::KeyPressure(
358 uchar channel
, uchar note
, uchar pressure
, uint32 time
)
361 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
368 BSoftSynth::ControlChange(
369 uchar channel
, uchar controlNumber
, uchar controlValue
, uint32 time
)
372 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
373 fluid_synth_cc(fSynth
, channel
- 1, controlNumber
, controlValue
);
379 BSoftSynth::ProgramChange(
380 uchar channel
, uchar programNumber
, uint32 time
)
383 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
384 fluid_synth_program_change(fSynth
, channel
- 1, programNumber
);
390 BSoftSynth::ChannelPressure(uchar channel
, uchar pressure
, uint32 time
)
393 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
400 BSoftSynth::PitchBend(uchar channel
, uchar lsb
, uchar msb
, uint32 time
)
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));
412 BSoftSynth::SystemExclusive(void* data
, size_t length
, uint32 time
)
415 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
422 BSoftSynth::SystemCommon(
423 uchar status
, uchar data1
, uchar data2
, uint32 time
)
426 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
433 BSoftSynth::SystemRealTime(uchar status
, uint32 time
)
436 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
443 BSoftSynth::TempoChange(int32 beatsPerMinute
, uint32 time
)
446 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
453 BSoftSynth::AllNotesOff(bool justChannel
, uint32 time
)
456 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
458 // from BMidi::AllNotesOff
459 for (uchar channel
= 1; channel
<= 16; ++channel
) {
460 fluid_synth_cc(fSynth
, channel
- 1, B_ALL_NOTES_OFF
, 0);
463 for (uchar note
= 0; note
<= 0x7F; ++note
) {
464 fluid_synth_noteoff(fSynth
, channel
- 1, note
);
480 fSettings
= new_fluid_settings();
481 fluid_settings_setnum(fSettings
, (char*)"synth.sample-rate", fSampleRate
);
482 fluid_settings_setint(fSettings
, (char*)"synth.polyphony", fMaxVoices
);
484 fSynth
= new_fluid_synth(fSettings
);
488 err
= fluid_synth_sfload(fSynth
, fInstrumentsFile
, 1);
490 fprintf(stderr
, "error in fluid_synth_sfload\n");
494 SetReverb(fReverbMode
);
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
;
501 fSoundPlayer
= new BSoundPlayer(&format
, "Soft Synth", &PlayBuffer
, NULL
, this);
502 err
= fSoundPlayer
->InitCheck();
504 fprintf(stderr
, "error in BSoundPlayer\n");
508 fSoundPlayer
->SetHasData(true);
509 fSoundPlayer
->Start();
519 fSoundPlayer
->SetHasData(false);
520 fSoundPlayer
->Stop();
525 delete_fluid_synth(fSynth
);
529 delete_fluid_settings(fSettings
);
536 BSoftSynth::PlayBuffer(void* cookie
, void* data
, size_t size
,
537 const media_raw_audio_format
& format
)
539 BSoftSynth
* synth
= (BSoftSynth
*)cookie
;
541 if (synth
->fMonitorSize
== 0) {
542 synth
->fMonitor
= (float*)new void*[size
];
543 synth
->fMonitorSize
= size
;
544 synth
->fMonitorChans
= format
.channel_count
;
547 // we use float samples
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
);
554 memcpy(synth
->fMonitor
, data
, size
);