Added preliminary support for using TiMidity++ as softsynth.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11678 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+146
-29
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2004 Matthijs Hollemans
|
* Copyright (c) 2004-2005 Matthijs Hollemans
|
||||||
* Copyright (c) 2003 Jerome Leveque
|
* Copyright (c) 2003 Jerome Leveque
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
@@ -21,12 +21,16 @@
|
|||||||
* DEALINGS IN THE SOFTWARE.
|
* DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <MidiRoster.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>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "MidiGlue.h" // for MAKE_BIGTIME
|
||||||
#include "SoftSynth.h"
|
#include "SoftSynth.h"
|
||||||
|
|
||||||
using namespace BPrivate;
|
using namespace BPrivate;
|
||||||
@@ -45,13 +49,29 @@ BSoftSynth::BSoftSynth()
|
|||||||
reverbEnabled = true;
|
reverbEnabled = true;
|
||||||
reverbMode = B_REVERB_BALLROOM;
|
reverbMode = B_REVERB_BALLROOM;
|
||||||
volumeScale = 1.0;
|
volumeScale = 1.0;
|
||||||
|
|
||||||
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
BSoftSynth::~BSoftSynth()
|
BSoftSynth::~BSoftSynth()
|
||||||
{
|
{
|
||||||
|
// Note: it is possible that we don't get deleted. When BSynth is
|
||||||
|
// created, it is assigned to the global variable be_synth. While
|
||||||
|
// BSynth is alive, it keeps a copy of BSoftSynth around too. Not
|
||||||
|
// a big deal, but the Midi Kit will complain (on stdout) that we
|
||||||
|
// didn't release our endpoints.
|
||||||
|
|
||||||
Unload();
|
Unload();
|
||||||
|
Done();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool BSoftSynth::InitCheck(void) const
|
||||||
|
{
|
||||||
|
return initCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -90,14 +110,10 @@ status_t BSoftSynth::SetDefaultInstrumentsFile()
|
|||||||
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;
|
||||||
}
|
|
||||||
|
|
||||||
if (IsLoaded())
|
if (IsLoaded())
|
||||||
{
|
|
||||||
Unload();
|
Unload();
|
||||||
}
|
|
||||||
|
|
||||||
instrumentsFile = strdup(path);
|
instrumentsFile = strdup(path);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -298,8 +314,12 @@ void BSoftSynth::Resume(void)
|
|||||||
void BSoftSynth::NoteOff(
|
void BSoftSynth::NoteOff(
|
||||||
uchar channel, uchar note, uchar velocity, uint32 time)
|
uchar channel, uchar note, uchar velocity, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayNoteOff(
|
||||||
|
channel - 1, note, velocity, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -307,8 +327,11 @@ void BSoftSynth::NoteOff(
|
|||||||
void BSoftSynth::NoteOn(
|
void BSoftSynth::NoteOn(
|
||||||
uchar channel, uchar note, uchar velocity, uint32 time)
|
uchar channel, uchar note, uchar velocity, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayNoteOn(channel - 1, note, velocity, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -316,8 +339,12 @@ void BSoftSynth::NoteOn(
|
|||||||
void BSoftSynth::KeyPressure(
|
void BSoftSynth::KeyPressure(
|
||||||
uchar channel, uchar note, uchar pressure, uint32 time)
|
uchar channel, uchar note, uchar pressure, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayKeyPressure(
|
||||||
|
channel - 1, note, pressure, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -325,8 +352,12 @@ void BSoftSynth::KeyPressure(
|
|||||||
void BSoftSynth::ControlChange(
|
void BSoftSynth::ControlChange(
|
||||||
uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
|
uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayControlChange(
|
||||||
|
channel - 1, controlNumber, controlValue, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -334,32 +365,46 @@ void BSoftSynth::ControlChange(
|
|||||||
void BSoftSynth::ProgramChange(
|
void BSoftSynth::ProgramChange(
|
||||||
uchar channel, uchar programNumber, uint32 time)
|
uchar channel, uchar programNumber, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayProgramChange(
|
||||||
|
channel - 1, programNumber, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
|
void BSoftSynth::ChannelPressure(uchar channel, uchar pressure, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayChannelPressure(
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayPitchBend(channel - 1, lsb, msb, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
|
void BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SpraySystemExclusive(data, length, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -367,32 +412,104 @@ void BSoftSynth::SystemExclusive(void* data, size_t length, uint32 time)
|
|||||||
void BSoftSynth::SystemCommon(
|
void BSoftSynth::SystemCommon(
|
||||||
uchar status, uchar data1, uchar data2, uint32 time)
|
uchar status, uchar data1, uchar data2, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SpraySystemCommon(status, data1, data2, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void BSoftSynth::SystemRealTime(uchar status, uint32 time)
|
void BSoftSynth::SystemRealTime(uchar status, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SpraySystemRealTime(status, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
|
void BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
producer->SprayTempoChange(beatsPerMinute, MAKE_BIGTIME(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
|
void BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
|
||||||
{
|
{
|
||||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
if (InitCheck())
|
||||||
UNIMPLEMENTED
|
{
|
||||||
|
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||||
|
|
||||||
|
// from BMidi::AllNotesOff
|
||||||
|
for (uchar channel = 1; channel <= 16; ++channel)
|
||||||
|
{
|
||||||
|
producer->SprayControlChange(channel, B_ALL_NOTES_OFF, 0, time);
|
||||||
|
|
||||||
|
if (!justChannel)
|
||||||
|
{
|
||||||
|
for (uchar note = 0; note <= 0x7F; ++note)
|
||||||
|
{
|
||||||
|
producer->SprayNoteOff(channel, note, 0, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void BSoftSynth::Init()
|
||||||
|
{
|
||||||
|
initCheck = 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();
|
||||||
|
producer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2004 Matthijs Hollemans
|
* Copyright (c) 2004-2005 Matthijs Hollemans
|
||||||
* Copyright (c) 2003 Jerome Leveque
|
* Copyright (c) 2003 Jerome Leveque
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
@@ -24,15 +24,50 @@
|
|||||||
#ifndef _SOFT_SYNTH_H
|
#ifndef _SOFT_SYNTH_H
|
||||||
#define _SOFT_SYNTH_H
|
#define _SOFT_SYNTH_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
WORK IN PROGRESS!
|
||||||
|
|
||||||
|
This version of SoftSynth is a wrapper for Michael Pfeiffer's port
|
||||||
|
of TiMidity++ 2.11.3/3 (http://www.bebits.com/app/2736).
|
||||||
|
|
||||||
|
It works, but not good enough yet. Playback from MidiPlayer sounds
|
||||||
|
a lot worse than using TiMidity in standalone mode. Either TiMidity's
|
||||||
|
MidiConsumer doesn't work properly or the Midi Kit messes things up;
|
||||||
|
I haven't investigated yet.
|
||||||
|
|
||||||
|
To try it out, download TiMidity and the sound files (30MB):
|
||||||
|
http://bepdf.sourceforge.net/download/midi/TiMidity++-2.11.3_3.x86.zip
|
||||||
|
http://bepdf.sourceforge.net/download/midi/eawpats11_full_beos.zip
|
||||||
|
|
||||||
|
Follow the instructions in the archive to install. Then double-click
|
||||||
|
"Start TiMidity Server" to launch TiMidity. The server will publish a
|
||||||
|
consumer endpoint. You can verify this with PatchBay.
|
||||||
|
|
||||||
|
Build the Haiku Midi Kit. Put libmidi.so and libmidi2.so in ~/config/lib.
|
||||||
|
Quit the BeOS midi_server. Launch the Haiku midi_server.
|
||||||
|
|
||||||
|
Build the Haiku MidiPlayer (or use the BeOS MidiPlayer). Start it and
|
||||||
|
choose a MIDI file. If all went fine, you will hear TiMidity play back
|
||||||
|
the song. Just not very well. :-)
|
||||||
|
|
||||||
|
Note: You can still use the Midi Kit if you don't install TiMidity,
|
||||||
|
but the software synth will simply make no sound.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <Midi.h>
|
#include <Midi.h>
|
||||||
#include <Synth.h>
|
#include <Synth.h>
|
||||||
|
|
||||||
|
class BMidiConsumer;
|
||||||
|
class BMidiLocalProducer;
|
||||||
|
|
||||||
namespace BPrivate {
|
namespace BPrivate {
|
||||||
|
|
||||||
class BSoftSynth
|
class BSoftSynth
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
bool InitCheck(void) const;
|
||||||
|
|
||||||
void Unload(void);
|
void Unload(void);
|
||||||
bool IsLoaded(void) const;
|
bool IsLoaded(void) const;
|
||||||
|
|
||||||
@@ -89,6 +124,10 @@ private:
|
|||||||
BSoftSynth();
|
BSoftSynth();
|
||||||
~BSoftSynth();
|
~BSoftSynth();
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Done();
|
||||||
|
|
||||||
|
bool initCheck;
|
||||||
char* instrumentsFile;
|
char* instrumentsFile;
|
||||||
int32 sampleRate;
|
int32 sampleRate;
|
||||||
interpolation_mode interpMode;
|
interpolation_mode interpMode;
|
||||||
@@ -97,6 +136,21 @@ private:
|
|||||||
reverb_mode reverbMode;
|
reverb_mode reverbMode;
|
||||||
bool reverbEnabled;
|
bool reverbEnabled;
|
||||||
double volumeScale;
|
double volumeScale;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note: Maybe this isn't the most efficient way to do things.
|
||||||
|
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;
|
||||||
|
BMidiLocalProducer* producer;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace BPrivate
|
} // namespace BPrivate
|
||||||
|
|||||||
Reference in New Issue
Block a user