an untested work in progress of BSoftSynth using fluidsynth (it's a try out and could be reverted if we feel like it)

libfluidsynth.so isn't on the image yet


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17844 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Jérôme Duval
2006-06-15 16:03:18 +00:00
parent 23b47e0b5e
commit e3a74a24bd
3 changed files with 219 additions and 243 deletions
+3
View File
@@ -1,6 +1,7 @@
SubDir HAIKU_TOP src kits midi ; SubDir HAIKU_TOP src kits midi ;
UsePrivateHeaders midi ; UsePrivateHeaders midi ;
UseLibraryHeaders fluidsynth ;
SharedLibrary libmidi.so : SharedLibrary libmidi.so :
Midi.cpp Midi.cpp
@@ -16,4 +17,6 @@ SharedLibrary libmidi.so :
: :
be be
midi2 midi2
media
libfluidsynth.so
; ;
+197 -217
View File
@@ -23,7 +23,6 @@
#include <MidiRoster.h> #include <MidiRoster.h>
#include <MidiConsumer.h> #include <MidiConsumer.h>
#include <MidiProducer.h>
#include <FindDirectory.h> #include <FindDirectory.h>
#include <Path.h> #include <Path.h>
#include <string.h> #include <string.h>
@@ -35,25 +34,25 @@
using namespace BPrivate; using namespace BPrivate;
//------------------------------------------------------------------------------
BSoftSynth::BSoftSynth() BSoftSynth::BSoftSynth()
: fSynth(NULL),
fSettings(NULL),
fSoundPlayer(NULL)
{ {
instrumentsFile = NULL; fInstrumentsFile = NULL;
SetDefaultInstrumentsFile(); SetDefaultInstrumentsFile();
sampleRate = 22050; fSampleRate = 44100;
interpMode = B_LINEAR_INTERPOLATION; fInterpMode = B_LINEAR_INTERPOLATION;
maxVoices = 28; fMaxVoices = 256;
limiterThreshold = 7; fLimiterThreshold = 7;
reverbEnabled = true; fReverbEnabled = true;
reverbMode = B_REVERB_BALLROOM; fReverbMode = B_REVERB_BALLROOM;
volumeScale = 1.0;
Init(); Init();
} }
//------------------------------------------------------------------------------
BSoftSynth::~BSoftSynth() BSoftSynth::~BSoftSynth()
{ {
@@ -67,37 +66,36 @@ BSoftSynth::~BSoftSynth()
Done(); Done();
} }
//------------------------------------------------------------------------------
bool BSoftSynth::InitCheck(void) const bool
BSoftSynth::InitCheck(void) const
{ {
return initCheck; return fInitCheck;
} }
//------------------------------------------------------------------------------
void BSoftSynth::Unload(void) void
BSoftSynth::Unload(void)
{ {
/* TODO: purge samples from memory */ /* TODO: purge samples from memory */
free(instrumentsFile); free(fInstrumentsFile);
instrumentsFile = NULL; fInstrumentsFile = NULL;
} }
//------------------------------------------------------------------------------
bool BSoftSynth::IsLoaded(void) const bool
BSoftSynth::IsLoaded(void) const
{ {
return instrumentsFile != NULL; return fInstrumentsFile != NULL;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::SetDefaultInstrumentsFile() status_t
BSoftSynth::SetDefaultInstrumentsFile()
{ {
BPath path; BPath path;
if (B_OK == find_directory(B_SYNTH_DIRECTORY, &path, false, NULL)) if (B_OK == find_directory(B_SYNTH_DIRECTORY, &path, false, NULL)) {
{
path.Append(B_BIG_SYNTH_FILE); path.Append(B_BIG_SYNTH_FILE);
return SetInstrumentsFile(path.Path()); return SetInstrumentsFile(path.Path());
} }
@@ -105,9 +103,9 @@ status_t BSoftSynth::SetDefaultInstrumentsFile()
return B_ERROR; return B_ERROR;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::SetInstrumentsFile(const char* path) status_t
BSoftSynth::SetInstrumentsFile(const char* path)
{ {
if (path == NULL) if (path == NULL)
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -115,13 +113,13 @@ status_t BSoftSynth::SetInstrumentsFile(const char* path)
if (IsLoaded()) if (IsLoaded())
Unload(); Unload();
instrumentsFile = strdup(path); fInstrumentsFile = strdup(path);
return B_OK; return B_OK;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::LoadAllInstruments() status_t
BSoftSynth::LoadAllInstruments()
{ {
/* TODO: Should load all of the instruments from the sample bank. */ /* TODO: Should load all of the instruments from the sample bank. */
@@ -129,387 +127,369 @@ status_t BSoftSynth::LoadAllInstruments()
return B_OK; return B_OK;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::LoadInstrument(int16 instrument) status_t
BSoftSynth::LoadInstrument(int16 instrument)
{ {
UNIMPLEMENTED UNIMPLEMENTED
return B_OK; return B_OK;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::UnloadInstrument(int16 instrument) status_t
BSoftSynth::UnloadInstrument(int16 instrument)
{ {
UNIMPLEMENTED UNIMPLEMENTED
return B_OK; return B_OK;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::RemapInstrument(int16 from, int16 to) status_t
BSoftSynth::RemapInstrument(int16 from, int16 to)
{ {
UNIMPLEMENTED UNIMPLEMENTED
return B_OK; return B_OK;
} }
//------------------------------------------------------------------------------
void BSoftSynth::FlushInstrumentCache(bool startStopCache) void
BSoftSynth::FlushInstrumentCache(bool startStopCache)
{ {
// TODO: we may decide not to support this function because it's weird! // TODO: we may decide not to support this function because it's weird!
UNIMPLEMENTED UNIMPLEMENTED
} }
//------------------------------------------------------------------------------
void BSoftSynth::SetVolume(double volume) void
BSoftSynth::SetVolume(double volume)
{ {
if (volume >= 0.0) if (volume >= 0.0) {
{ fluid_synth_set_gain(fSynth, volume);
volumeScale = volume;
} }
} }
//------------------------------------------------------------------------------
double BSoftSynth::Volume(void) const double
BSoftSynth::Volume(void) const
{ {
return volumeScale; return fluid_synth_get_gain(fSynth);
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::SetSamplingRate(int32 rate) status_t
BSoftSynth::SetSamplingRate(int32 rate)
{ {
// TODO: According to the BeBook, we should round rate to the nearest if (rate == 22050 || rate == 44100 || rate == 48000) {
// acceptable value. However, this function may change depending on the fSampleRate = rate;
// softsynth back-end we'll use. Init();
if (rate == 11025 || rate == 22050 || rate == 44100)
{
sampleRate = rate;
return B_OK; return B_OK;
} }
return B_BAD_VALUE; return B_BAD_VALUE;
} }
//------------------------------------------------------------------------------
int32 BSoftSynth::SamplingRate() const int32
BSoftSynth::SamplingRate() const
{ {
return sampleRate; return fSampleRate;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::SetInterpolation(interpolation_mode mode) status_t
BSoftSynth::SetInterpolation(interpolation_mode mode)
{ {
// TODO: this function could change depending on the synth back-end. // not used because our synth uses the same format than the soundplayer
fInterpMode = mode;
interpMode = mode;
return B_OK; return B_OK;
} }
//------------------------------------------------------------------------------
interpolation_mode BSoftSynth::Interpolation() const interpolation_mode
BSoftSynth::Interpolation() const
{ {
return interpMode; return fInterpMode;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::EnableReverb(bool enabled) status_t
BSoftSynth::EnableReverb(bool enabled)
{ {
reverbEnabled = enabled; fReverbEnabled = enabled;
fluid_synth_set_reverb_on(fSynth, enabled);
return B_OK; return B_OK;
} }
//------------------------------------------------------------------------------
bool BSoftSynth::IsReverbEnabled() const bool
BSoftSynth::IsReverbEnabled() const
{ {
return reverbEnabled; return fReverbEnabled;
} }
//------------------------------------------------------------------------------
void BSoftSynth::SetReverb(reverb_mode mode) void
BSoftSynth::SetReverb(reverb_mode mode)
{ {
// TODO: this function could change depending on the synth back-end. // TODO: this function could change depending on the synth back-end.
reverbMode = mode; fReverbMode = mode;
} }
//------------------------------------------------------------------------------
reverb_mode BSoftSynth::Reverb() const reverb_mode
BSoftSynth::Reverb() const
{ {
return reverbMode; return fReverbMode;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::SetMaxVoices(int32 max) status_t
BSoftSynth::SetMaxVoices(int32 max)
{ {
// TODO: this function could change depending on the synth back-end. if (max > 0 && max <= 4096) {
fMaxVoices = max;
if (max > 0 && max <= 32) Init();
{
maxVoices = max;
return B_OK; return B_OK;
} }
return B_BAD_VALUE; return B_BAD_VALUE;
} }
//------------------------------------------------------------------------------
int16 BSoftSynth::MaxVoices(void) const int16
BSoftSynth::MaxVoices(void) const
{ {
return maxVoices; return fMaxVoices;
} }
//------------------------------------------------------------------------------
status_t BSoftSynth::SetLimiterThreshold(int32 threshold) status_t
BSoftSynth::SetLimiterThreshold(int32 threshold)
{ {
// TODO: this function could change depending on the synth back-end. // not used
if (threshold > 0 && threshold <= 32) {
if (threshold > 0 && threshold <= 32) fLimiterThreshold = threshold;
{
limiterThreshold = threshold;
return B_OK; return B_OK;
} }
return B_BAD_VALUE; return B_BAD_VALUE;
} }
//------------------------------------------------------------------------------
int16 BSoftSynth::LimiterThreshold(void) const int16
BSoftSynth::LimiterThreshold(void) const
{ {
return limiterThreshold; return fLimiterThreshold;
} }
//------------------------------------------------------------------------------
void BSoftSynth::Pause(void) void
BSoftSynth::Pause(void)
{ {
UNIMPLEMENTED UNIMPLEMENTED
} }
//------------------------------------------------------------------------------
void BSoftSynth::Resume(void) void
BSoftSynth::Resume(void)
{ {
UNIMPLEMENTED UNIMPLEMENTED
} }
//------------------------------------------------------------------------------
void BSoftSynth::NoteOff( void
BSoftSynth::NoteOff(
uchar channel, uchar note, uchar velocity, uint32 time) uchar channel, uchar note, uchar velocity, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayNoteOff( fluid_synth_noteoff(fSynth, channel - 1, note); // velocity isn't used in FS
channel - 1, note, velocity, MAKE_BIGTIME(time));
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::NoteOn( void
BSoftSynth::NoteOn(
uchar channel, uchar note, uchar velocity, uint32 time) uchar channel, uchar note, uchar velocity, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayNoteOn(channel - 1, note, velocity, MAKE_BIGTIME(time)); fluid_synth_noteon(fSynth, channel - 1, note, velocity);
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::KeyPressure( void
BSoftSynth::KeyPressure(
uchar channel, uchar note, uchar pressure, uint32 time) uchar channel, uchar note, uchar pressure, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayKeyPressure( // unavailable
channel - 1, note, pressure, MAKE_BIGTIME(time));
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::ControlChange( void
BSoftSynth::ControlChange(
uchar channel, uchar controlNumber, uchar controlValue, uint32 time) uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayControlChange( fluid_synth_cc(fSynth, channel - 1, controlNumber, controlValue);
channel - 1, controlNumber, controlValue, MAKE_BIGTIME(time));
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::ProgramChange( void
BSoftSynth::ProgramChange(
uchar channel, uchar programNumber, uint32 time) uchar channel, uchar programNumber, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayProgramChange( fluid_synth_program_change(fSynth, channel - 1, programNumber);
channel - 1, programNumber, MAKE_BIGTIME(time));
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time) void
BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayChannelPressure( //unavailable
channel - 1, pressure, MAKE_BIGTIME(time));
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time) void
BSoftSynth::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayPitchBend(channel - 1, lsb, msb, MAKE_BIGTIME(time)); // fluid_synth only accepts an int
fluid_synth_pitch_bend(fSynth, channel - 1, ((uint32)(msb & 0x7f) << 8) | (lsb & 0x7f));
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time) void
BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SpraySystemExclusive(data, length, MAKE_BIGTIME(time)); // unsupported
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::SystemCommon( void
BSoftSynth::SystemCommon(
uchar status, uchar data1, uchar data2, uint32 time) uchar status, uchar data1, uchar data2, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SpraySystemCommon(status, data1, data2, MAKE_BIGTIME(time)); // unsupported
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::SystemRealTime(uchar status, uint32 time) void
BSoftSynth::SystemRealTime(uchar status, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SpraySystemRealTime(status, MAKE_BIGTIME(time)); // unsupported
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time) void
BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
producer->SprayTempoChange(beatsPerMinute, MAKE_BIGTIME(time)); // unsupported
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::AllNotesOff(bool justChannel, uint32 time) void
BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
{ {
if (InitCheck()) if (InitCheck()) {
{
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE); snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
// from BMidi::AllNotesOff // from BMidi::AllNotesOff
for (uchar channel = 1; channel <= 16; ++channel) for (uchar channel = 1; channel <= 16; ++channel) {
{ fluid_synth_cc(fSynth, channel - 1, B_ALL_NOTES_OFF, 0);
producer->SprayControlChange(channel, B_ALL_NOTES_OFF, 0, time);
if (!justChannel) if (!justChannel) {
{ for (uchar note = 0; note <= 0x7F; ++note) {
for (uchar note = 0; note <= 0x7F; ++note) fluid_synth_noteoff(fSynth, channel - 1, note);
{
producer->SprayNoteOff(channel, note, 0, time);
} }
} }
} }
} }
} }
//------------------------------------------------------------------------------
void BSoftSynth::Init() void
BSoftSynth::Init()
{ {
initCheck = false; fInitCheck = false;
producer = new BMidiLocalProducer("TiMidity feeder");
int32 id = 0;
while ((consumer = BMidiRoster::NextConsumer(&id)) != NULL)
{
if (strcmp(consumer->Name(), "TiMidity input") == 0)
break;
consumer->Release();
}
if (consumer == NULL)
{
fprintf(stderr, "[midi] TiMidity consumer not found\n");
return;
}
if (producer->Connect(consumer) != B_OK)
{
fprintf(stderr, "[midi] Could not connect to TiMidity\n");
return;
}
initCheck = true;
}
//------------------------------------------------------------------------------
void BSoftSynth::Done()
{
if (consumer != NULL)
{
producer->Disconnect(consumer);
consumer->Release();
consumer = NULL;
}
producer->Release(); Done();
producer = NULL;
fSettings = new_fluid_settings();
fluid_settings_setnum(fSettings, "synth.sample-rate", fSampleRate);
fluid_settings_setint(fSettings, "synth.polyphony", fMaxVoices);
fSynth = new_fluid_synth(fSettings);
media_raw_audio_format format;
format.channel_count = 2;
format.frame_rate = fSampleRate;
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
fSoundPlayer = new BSoundPlayer(&format, "softsynth", &PlayBuffer, NULL, this);
status_t err = fSoundPlayer->InitCheck();
if (err != B_OK)
return;
fSoundPlayer->SetHasData(true);
fSoundPlayer->Start();
fInitCheck = true;
}
void
BSoftSynth::Done()
{
if (fSoundPlayer) {
fSoundPlayer->SetHasData(false);
fSoundPlayer->Stop();
delete fSoundPlayer;
}
if (fSynth)
delete_fluid_synth(fSynth);
if (fSettings)
delete_fluid_settings(fSettings);
}
void
BSoftSynth::PlayBuffer(void * cookie, void * data, size_t size, const media_raw_audio_format & format)
{
BSoftSynth *synth = (BSoftSynth *)cookie;
// we use float samples
fluid_synth_write_float(synth->fSynth, size / sizeof(float) / format.channel_count,
data, 0, sizeof(float) * format.channel_count,
(char *)data + sizeof(float), 0, sizeof(float) * format.channel_count);
} }
//------------------------------------------------------------------------------
+19 -26
View File
@@ -57,8 +57,11 @@
#include <Midi.h> #include <Midi.h>
#include <Synth.h> #include <Synth.h>
#include <SoundPlayer.h>
#include <fluidsynth.h>
class BMidiConsumer; class BMidiConsumer;
class BMidiLocalProducer;
class BMidiSynth; class BMidiSynth;
class BSynth; class BSynth;
@@ -68,10 +71,10 @@ class BSoftSynth
{ {
public: public:
bool InitCheck(void) const; bool InitCheck() const;
void Unload(void); void Unload();
bool IsLoaded(void) const; bool IsLoaded() const;
status_t SetDefaultInstrumentsFile(); status_t SetDefaultInstrumentsFile();
status_t SetInstrumentsFile(const char* path); status_t SetInstrumentsFile(const char* path);
@@ -128,31 +131,21 @@ private:
void Init(); void Init();
void Done(); void Done();
static void PlayBuffer(void * cookie, void * data, size_t size, const media_raw_audio_format & format);
bool initCheck; bool fInitCheck;
char* instrumentsFile; char* fInstrumentsFile;
int32 sampleRate; int32 fSampleRate;
interpolation_mode interpMode; interpolation_mode fInterpMode;
int16 maxVoices; int16 fMaxVoices;
int16 limiterThreshold; int16 fLimiterThreshold;
reverb_mode reverbMode; reverb_mode fReverbMode;
bool reverbEnabled; bool fReverbEnabled;
double volumeScale;
/* fluid_synth_t *fSynth;
Note: Maybe this isn't the most efficient way to do things. fluid_settings_t* fSettings;
Now a producer connects to BMidiSynth, which is a consumer.
That consumer directly calls our NoteOff() etc, methods.
We create a new producer and connect it to TiMidity's consumer.
It would save some indirection if BMidiSynth's consumer would
be a proxy for TiMidity's. (I don't think that is possible,
because BMidiSynth is a BMidi object which always creates a
new consumer regardless. In any case, notes have to travel
a long way before they reach TiMidity.
*/
BMidiConsumer* consumer; BSoundPlayer *fSoundPlayer;
BMidiLocalProducer* producer;
}; };
} // namespace BPrivate } // namespace BPrivate