* Copyright 2006-2014, Haiku.
*
* Copyright (c) 2004-2005 Matthijs Hollemans
* Copyright (c) 2003 Jerome Leveque
* Distributed under the terms of the MIT License.
*
* Authors:
* Jérôme Duval
* Jérôme Leveque
* Matthijs Hollemans
* Pete Goodeve
*/
#include <MidiRoster.h>
#include <MidiConsumer.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <Node.h>
#include <NodeInfo.h>
#include <Path.h>
#include <PathFinder.h>
#include <string.h>
#include <stdlib.h>
#include <MidiSettings.h>
#include "debug.h"
#include "MidiGlue.h"
#include "SoftSynth.h"
using namespace BPrivate;
struct ReverbSettings {
double room, damp, width, level;
} gReverbSettings[] = {
{0.0, 0.0, 0.0, 0.0},
{0.2, 0.0, 0.5, 0.9},
{0.5, 0.0, 0.9, 0.9},
{0.7, 0.25, 0.9, 0.95},
{0.99, 0.3, 1.0, 1.0},
{1.03, 0.6, 1.0, 1.0}
};
BSoftSynth::BSoftSynth()
: fInitCheck(false),
fSynth(NULL),
fSettings(NULL),
fSoundPlayer(NULL),
fMonitor(NULL),
fMonitorSize(0),
fMonitorChans(-1)
{
fInstrumentsFile = NULL;
SetDefaultInstrumentsFile();
fSampleRate = 44100;
fInterpMode = B_LINEAR_INTERPOLATION;
fMaxVoices = 256;
fLimiterThreshold = 7;
fReverbEnabled = true;
fReverbMode = B_REVERB_BALLROOM;
}
BSoftSynth::~BSoftSynth()
{
delete[] fMonitor;
Unload();
}
bool
BSoftSynth::InitCheck()
{
if (!fSynth)
_Init();
return fInitCheck;
}
void
BSoftSynth::Unload(void)
{
_Done();
free(fInstrumentsFile);
fInstrumentsFile = NULL;
}
bool
BSoftSynth::IsLoaded(void) const
{
return fInstrumentsFile != NULL;
}
status_t
BSoftSynth::SetDefaultInstrumentsFile()
{
struct BPrivate::midi_settings settings;
if (BPrivate::read_midi_settings(&settings) == B_OK) {
if (SetInstrumentsFile(settings.soundfont_file) == B_OK)
return B_OK;
}
BPath path;
if (find_directory(B_SYNTH_DIRECTORY, &path, false, NULL) == B_OK) {
path.Append("synth/TimGM6mb.sf2");
if (SetInstrumentsFile(path.Path()) == B_OK)
return B_OK;
}
BStringList paths;
status_t status = BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY,
"synth", paths);
if (status != B_OK)
return B_ERROR;
for (int32 i = 0; i < paths.CountStrings(); i++) {
BDirectory directory(paths.StringAt(i).String());
BEntry entry;
if (directory.InitCheck() != B_OK)
continue;
while (directory.GetNextEntry(&entry) == B_OK) {
BNode node(&entry);
BNodeInfo nodeInfo(&node);
char mimeType[B_MIME_TYPE_LENGTH];
if (nodeInfo.GetType(mimeType) == B_OK
) {
BPath fullPath = paths.StringAt(i).String();
fullPath.Append(entry.Name());
if (SetInstrumentsFile(fullPath.Path()) == B_OK)
return B_OK;
}
}
}
return B_ERROR;
}
status_t
BSoftSynth::SetInstrumentsFile(const char* path)
{
if (path == NULL)
return B_BAD_VALUE;
if (!BEntry(path).Exists())
return B_ENTRY_NOT_FOUND;
if (IsLoaded())
Unload();
fInstrumentsFile = strdup(path);
return B_OK;
}
status_t
BSoftSynth::LoadAllInstruments()
{
InitCheck();
return B_OK;
}
status_t
BSoftSynth::LoadInstrument(int16 instrument)
{
UNIMPLEMENTED
return B_OK;
}
status_t
BSoftSynth::UnloadInstrument(int16 instrument)
{
UNIMPLEMENTED
return B_OK;
}
status_t
BSoftSynth::RemapInstrument(int16 from, int16 to)
{
UNIMPLEMENTED
return B_OK;
}
void
BSoftSynth::FlushInstrumentCache(bool startStopCache)
{
UNIMPLEMENTED
}
void
BSoftSynth::SetVolume(double volume)
{
if (InitCheck())
if (volume >= 0.0) {
fluid_synth_set_gain(fSynth, volume);
}
}
double
BSoftSynth::Volume(void) const
{
return fluid_synth_get_gain(fSynth);
}
status_t
BSoftSynth::SetSamplingRate(int32 rate)
{
if (rate == 22050 || rate == 44100 || rate == 48000) {
fSampleRate = rate;
return B_OK;
}
return B_BAD_VALUE;
}
int32
BSoftSynth::SamplingRate() const
{
return fSampleRate;
}
status_t
BSoftSynth::SetInterpolation(interpolation_mode mode)
{
fInterpMode = mode;
return B_OK;
}
interpolation_mode
BSoftSynth::Interpolation() const
{
return fInterpMode;
}
status_t
BSoftSynth::EnableReverb(bool enabled)
{
fReverbEnabled = enabled;
fluid_synth_set_reverb_on(fSynth, enabled);
return B_OK;
}
bool
BSoftSynth::IsReverbEnabled() const
{
return fReverbEnabled;
}
void
BSoftSynth::SetReverb(reverb_mode mode)
{
if (mode < B_REVERB_NONE || mode > B_REVERB_DUNGEON)
return;
fReverbMode = mode;
if (fSynth) {
ReverbSettings *rvb = &gReverbSettings[mode - 1];
fluid_synth_set_reverb(fSynth, rvb->room, rvb->damp, rvb->width,
rvb->level);
}
}
reverb_mode
BSoftSynth::Reverb() const
{
return fReverbMode;
}
status_t
BSoftSynth::SetMaxVoices(int32 max)
{
if (max > 0 && max <= 4096) {
fMaxVoices = max;
return B_OK;
}
return B_BAD_VALUE;
}
int16
BSoftSynth::MaxVoices(void) const
{
return fMaxVoices;
}
status_t
BSoftSynth::SetLimiterThreshold(int32 threshold)
{
if (threshold > 0 && threshold <= 32) {
fLimiterThreshold = threshold;
return B_OK;
}
return B_BAD_VALUE;
}
int16
BSoftSynth::LimiterThreshold(void) const
{
return fLimiterThreshold;
}
void
BSoftSynth::Pause(void)
{
UNIMPLEMENTED
}
void
BSoftSynth::Resume(void)
{
UNIMPLEMENTED
}
void
BSoftSynth::NoteOff(
uchar channel, uchar note, uchar velocity, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
fluid_synth_noteoff(fSynth, channel - 1, note);
}
}
void
BSoftSynth::NoteOn(
uchar channel, uchar note, uchar velocity, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
fluid_synth_noteon(fSynth, channel - 1, note, velocity);
}
}
void
BSoftSynth::KeyPressure(
uchar channel, uchar note, uchar pressure, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
}
}
void
BSoftSynth::ControlChange(
uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
fluid_synth_cc(fSynth, channel - 1, controlNumber, controlValue);
}
}
void
BSoftSynth::ProgramChange(
uchar channel, uchar programNumber, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
fluid_synth_program_change(fSynth, channel - 1, programNumber);
}
}
void
BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
}
}
void
BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
fluid_synth_pitch_bend(fSynth, channel - 1,
((uint32)(msb & 0x7f) << 7) | (lsb & 0x7f));
}
}
void
BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
}
}
void
BSoftSynth::SystemCommon(
uchar status, uchar data1, uchar data2, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
}
}
void
BSoftSynth::SystemRealTime(uchar status, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
}
}
void
BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
}
}
void
BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
{
if (InitCheck()) {
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
for (uchar channel = 1; channel <= 16; ++channel) {
fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0);
if (!justChannel) {
for (uchar note = 0; note <= 0x7F; ++note) {
fluid_synth_noteoff(fSynth, channel - 1, note);
}
}
}
}
}
void
BSoftSynth::_Init()
{
status_t err;
fInitCheck = false;
_Done();
fSettings = new_fluid_settings();
fluid_settings_setnum(fSettings, (char*)"synth.sample-rate", fSampleRate);
fluid_settings_setint(fSettings, (char*)"synth.polyphony", fMaxVoices);
fSynth = new_fluid_synth(fSettings);
if (!fSynth)
return;
err = fluid_synth_sfload(fSynth, fInstrumentsFile, 1);
if (err < B_OK) {
fprintf(stderr, "error in fluid_synth_sfload\n");
return;
}
SetReverb(fReverbMode);
media_raw_audio_format format = media_raw_audio_format::wildcard;
format.channel_count = 2;
format.frame_rate = fSampleRate;
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
fSoundPlayer = new BSoundPlayer(&format, "Soft Synth", &PlayBuffer, NULL, this);
err = fSoundPlayer->InitCheck();
if (err != B_OK) {
fprintf(stderr, "error in BSoundPlayer\n");
return;
}
fSoundPlayer->SetHasData(true);
fSoundPlayer->Start();
fInitCheck = true;
}
void
BSoftSynth::_Done()
{
if (fSoundPlayer) {
fSoundPlayer->SetHasData(false);
fSoundPlayer->Stop();
delete fSoundPlayer;
fSoundPlayer = NULL;
}
if (fSynth) {
delete_fluid_synth(fSynth);
fSynth = NULL;
}
if (fSettings) {
delete_fluid_settings(fSettings);
fSettings = NULL;
}
}
void
BSoftSynth::PlayBuffer(void* cookie, void* data, size_t size,
const media_raw_audio_format& format)
{
BSoftSynth* synth = (BSoftSynth*)cookie;
if (synth->fMonitorSize == 0) {
synth->fMonitor = (float*)new void*[size];
synth->fMonitorSize = size;
synth->fMonitorChans = format.channel_count;
}
if (synth->fSynth) {
fluid_synth_write_float(
synth->fSynth, size / sizeof(float) / format.channel_count,
data, 0, format.channel_count,
data, 1, format.channel_count);
memcpy(synth->fMonitor, data, size);
}
}