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>
17 #include <Directory.h>
19 #include <FindDirectory.h>
23 #include <PathFinder.h>
28 #include <MidiSettings.h>
31 #include "MidiGlue.h" // for MAKE_BIGTIME
32 #include "SoftSynth.h"
34 using namespace BPrivate
;
36 struct 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
48 BSoftSynth::BSoftSynth()
57 fInstrumentsFile
= NULL
;
58 SetDefaultInstrumentsFile();
61 fInterpMode
= B_LINEAR_INTERPOLATION
;
63 fLimiterThreshold
= 7;
64 fReverbEnabled
= true;
65 fReverbMode
= B_REVERB_BALLROOM
;
69 BSoftSynth::~BSoftSynth()
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.
83 BSoftSynth::InitCheck()
92 BSoftSynth::Unload(void)
95 free(fInstrumentsFile
);
96 fInstrumentsFile
= NULL
;
101 BSoftSynth::IsLoaded(void) const
103 return fInstrumentsFile
!= NULL
;
108 BSoftSynth::SetDefaultInstrumentsFile()
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
115 struct BPrivate::midi_settings settings
;
116 if (BPrivate::read_midi_settings(&settings
) == B_OK
) {
117 if (SetInstrumentsFile(settings
.soundfont_file
) == B_OK
)
121 // Try a well-known (and usually present on a default install) soft synth
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
)
129 // Just use the first soundfont we find
131 status_t status
= BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY
,
137 for (int32 i
= 0; i
< paths
.CountStrings(); i
++) {
138 BDirectory
directory(paths
.StringAt(i
).String());
140 if (directory
.InitCheck() != B_OK
)
142 while (directory
.GetNextEntry(&entry
) == B_OK
) {
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
)
158 // TODO: Write the settings file ?
165 BSoftSynth::SetInstrumentsFile(const char* path
)
170 if (!BEntry(path
).Exists())
171 return B_FILE_NOT_FOUND
;
176 fInstrumentsFile
= strdup(path
);
182 BSoftSynth::LoadAllInstruments()
190 BSoftSynth::LoadInstrument(int16 instrument
)
198 BSoftSynth::UnloadInstrument(int16 instrument
)
206 BSoftSynth::RemapInstrument(int16 from
, int16 to
)
214 BSoftSynth::FlushInstrumentCache(bool startStopCache
)
216 // TODO: we may decide not to support this function because it's weird!
223 BSoftSynth::SetVolume(double volume
)
227 fluid_synth_set_gain(fSynth
, volume
);
233 BSoftSynth::Volume(void) const
235 return fluid_synth_get_gain(fSynth
);
240 BSoftSynth::SetSamplingRate(int32 rate
)
242 if (rate
== 22050 || rate
== 44100 || rate
== 48000) {
252 BSoftSynth::SamplingRate() const
259 BSoftSynth::SetInterpolation(interpolation_mode mode
)
261 // not used because our synth uses the same format than the soundplayer
268 BSoftSynth::Interpolation() const
275 BSoftSynth::EnableReverb(bool enabled
)
277 fReverbEnabled
= enabled
;
278 fluid_synth_set_reverb_on(fSynth
, enabled
);
284 BSoftSynth::IsReverbEnabled() const
286 return fReverbEnabled
;
291 BSoftSynth::SetReverb(reverb_mode mode
)
293 if (mode
< B_REVERB_NONE
|| mode
> B_REVERB_DUNGEON
)
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
,
307 BSoftSynth::Reverb() const
314 BSoftSynth::SetMaxVoices(int32 max
)
316 if (max
> 0 && max
<= 4096) {
326 BSoftSynth::MaxVoices(void) const
333 BSoftSynth::SetLimiterThreshold(int32 threshold
)
336 if (threshold
> 0 && threshold
<= 32) {
337 fLimiterThreshold
= threshold
;
346 BSoftSynth::LimiterThreshold(void) const
348 return fLimiterThreshold
;
353 BSoftSynth::Pause(void)
360 BSoftSynth::Resume(void)
368 uchar channel
, uchar note
, uchar velocity
, uint32 time
)
371 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
372 fluid_synth_noteoff(fSynth
, channel
- 1, note
); // velocity isn't used in FS
379 uchar channel
, uchar note
, uchar velocity
, uint32 time
)
382 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
383 fluid_synth_noteon(fSynth
, channel
- 1, note
, velocity
);
389 BSoftSynth::KeyPressure(
390 uchar channel
, uchar note
, uchar pressure
, uint32 time
)
393 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
400 BSoftSynth::ControlChange(
401 uchar channel
, uchar controlNumber
, uchar controlValue
, uint32 time
)
404 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
405 fluid_synth_cc(fSynth
, channel
- 1, controlNumber
, controlValue
);
411 BSoftSynth::ProgramChange(
412 uchar channel
, uchar programNumber
, uint32 time
)
415 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
416 fluid_synth_program_change(fSynth
, channel
- 1, programNumber
);
422 BSoftSynth::ChannelPressure(uchar channel
, uchar pressure
, uint32 time
)
425 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
432 BSoftSynth::PitchBend(uchar channel
, uchar lsb
, uchar msb
, uint32 time
)
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));
444 BSoftSynth::SystemExclusive(void* data
, size_t length
, uint32 time
)
447 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
454 BSoftSynth::SystemCommon(
455 uchar status
, uchar data1
, uchar data2
, uint32 time
)
458 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
465 BSoftSynth::SystemRealTime(uchar status
, uint32 time
)
468 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
475 BSoftSynth::TempoChange(int32 beatsPerMinute
, uint32 time
)
478 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
485 BSoftSynth::AllNotesOff(bool justChannel
, uint32 time
)
488 snooze_until(MAKE_BIGTIME(time
), B_SYSTEM_TIMEBASE
);
490 // from BMidi::AllNotesOff
491 for (uchar channel
= 1; channel
<= 16; ++channel
) {
492 fluid_synth_cc(fSynth
, channel
- 1, B_ALL_NOTES_OFF
, 0);
495 for (uchar note
= 0; note
<= 0x7F; ++note
) {
496 fluid_synth_noteoff(fSynth
, channel
- 1, note
);
512 fSettings
= new_fluid_settings();
513 fluid_settings_setnum(fSettings
, (char*)"synth.sample-rate", fSampleRate
);
514 fluid_settings_setint(fSettings
, (char*)"synth.polyphony", fMaxVoices
);
516 fSynth
= new_fluid_synth(fSettings
);
520 err
= fluid_synth_sfload(fSynth
, fInstrumentsFile
, 1);
522 fprintf(stderr
, "error in fluid_synth_sfload\n");
526 SetReverb(fReverbMode
);
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
;
533 fSoundPlayer
= new BSoundPlayer(&format
, "Soft Synth", &PlayBuffer
, NULL
, this);
534 err
= fSoundPlayer
->InitCheck();
536 fprintf(stderr
, "error in BSoundPlayer\n");
540 fSoundPlayer
->SetHasData(true);
541 fSoundPlayer
->Start();
551 fSoundPlayer
->SetHasData(false);
552 fSoundPlayer
->Stop();
557 delete_fluid_synth(fSynth
);
561 delete_fluid_settings(fSettings
);
568 BSoftSynth::PlayBuffer(void* cookie
, void* data
, size_t size
,
569 const media_raw_audio_format
& format
)
571 BSoftSynth
* synth
= (BSoftSynth
*)cookie
;
573 if (synth
->fMonitorSize
== 0) {
574 synth
->fMonitor
= (float*)new void*[size
];
575 synth
->fMonitorSize
= size
;
576 synth
->fMonitorChans
= format
.channel_count
;
579 // we use float samples
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
);
586 memcpy(synth
->fMonitor
, data
, size
);