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
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@@ -21,12 +21,16 @@
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <MidiRoster.h>
|
||||
#include <MidiConsumer.h>
|
||||
#include <MidiProducer.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <Path.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "MidiGlue.h" // for MAKE_BIGTIME
|
||||
#include "SoftSynth.h"
|
||||
|
||||
using namespace BPrivate;
|
||||
@@ -45,13 +49,29 @@ BSoftSynth::BSoftSynth()
|
||||
reverbEnabled = true;
|
||||
reverbMode = B_REVERB_BALLROOM;
|
||||
volumeScale = 1.0;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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();
|
||||
Done();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool BSoftSynth::InitCheck(void) const
|
||||
{
|
||||
return initCheck;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -90,14 +110,10 @@ status_t BSoftSynth::SetDefaultInstrumentsFile()
|
||||
status_t BSoftSynth::SetInstrumentsFile(const char* path)
|
||||
{
|
||||
if (path == NULL)
|
||||
{
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (IsLoaded())
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
instrumentsFile = strdup(path);
|
||||
return B_OK;
|
||||
@@ -298,8 +314,12 @@ void BSoftSynth::Resume(void)
|
||||
void BSoftSynth::NoteOff(
|
||||
uchar channel, uchar note, uchar velocity, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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(
|
||||
uchar channel, uchar note, uchar velocity, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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(
|
||||
uchar channel, uchar note, uchar pressure, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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(
|
||||
uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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(
|
||||
uchar channel, uchar programNumber, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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(
|
||||
uchar status, uchar data1, uchar data2, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||
producer->SpraySystemCommon(status, data1, data2, MAKE_BIGTIME(time));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void BSoftSynth::SystemRealTime(uchar status, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||
producer->SpraySystemRealTime(status, MAKE_BIGTIME(time));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void BSoftSynth::TempoChange(int32 beatsPerMinute, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
snooze_until(MAKE_BIGTIME(time), B_SYSTEM_TIMEBASE);
|
||||
producer->SprayTempoChange(beatsPerMinute, MAKE_BIGTIME(time));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void BSoftSynth::AllNotesOff(bool justChannel, uint32 time)
|
||||
{
|
||||
snooze_until(time * (bigtime_t) 1000, B_SYSTEM_TIMEBASE);
|
||||
UNIMPLEMENTED
|
||||
if (InitCheck())
|
||||
{
|
||||
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
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@@ -24,15 +24,50 @@
|
||||
#ifndef _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 <Synth.h>
|
||||
|
||||
class BMidiConsumer;
|
||||
class BMidiLocalProducer;
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
class BSoftSynth
|
||||
{
|
||||
public:
|
||||
|
||||
bool InitCheck(void) const;
|
||||
|
||||
void Unload(void);
|
||||
bool IsLoaded(void) const;
|
||||
|
||||
@@ -89,6 +124,10 @@ private:
|
||||
BSoftSynth();
|
||||
~BSoftSynth();
|
||||
|
||||
void Init();
|
||||
void Done();
|
||||
|
||||
bool initCheck;
|
||||
char* instrumentsFile;
|
||||
int32 sampleRate;
|
||||
interpolation_mode interpMode;
|
||||
@@ -97,6 +136,21 @@ private:
|
||||
reverb_mode reverbMode;
|
||||
bool reverbEnabled;
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user