1
#include "artsmodules.h"
2
#include "artsbuilder.h"
3
#include "stdsynthmodule.h"
4
#include "objectmanager.h"
6
#include "flowsystem.h"
14
/*-------- instrument mapping ---------*/
18
struct InstrumentData;
20
list<InstrumentData> instruments;
22
void loadLine(const string& line);
25
void loadFromList(const string& filename, const vector<string>& list);
26
StructureDesc getInstrument(mcopbyte channel, mcopbyte note,
27
mcopbyte velocity, mcopbyte program);
30
struct InstrumentMap::InstrumentData
34
int minValue, maxValue;
35
Range() : minValue(0), maxValue(0)
38
Range(int minValue, int maxValue)
39
: minValue(minValue), maxValue(maxValue)
44
return (value >= minValue) && (value <= maxValue);
47
Range channel, pitch, program, velocity;
48
StructureDesc instrument;
51
class InstrumentMap::Tokenizer {
53
bool haveToken, haveNextToken;
54
string token, nextToken, input;
57
Tokenizer(const string& line)
58
: haveToken(false), haveNextToken(false),
59
input(line+"\n"), ii(input.begin())
61
/* adding a \n ensures that we will definitely find the last token */
71
haveNextToken = false;
87
while(ii != input.end() && !haveToken)
89
const char& c = *ii++;
91
if(c == ' ' || c == '\t' || c == '\n')
93
if(token != "") haveToken = true;
95
else if(c == '=') /* || c == '-' || c == '+')*/
117
void InstrumentMap::loadLine(const string& line)
121
/* default: no filtering */
122
id.channel = InstrumentData::Range(0,15);
123
id.pitch = id.program = id.velocity = InstrumentData::Range(0,127);
130
if(t.getToken() != "ON")
132
arts_warning("error in arts-map: lines must start with ON (did start with %s)\n", t.getToken().c_str());
138
const string& token = t.getToken();
145
if(i == 2) /* evaluate */
149
arts_warning("error in arts-map: no = operator\n");
155
if(s[0] == "structure")
157
string filename = s[2];
159
/* if it's no absolute path, its relative to the map */
160
if(!filename.empty() && filename[0] != '/')
161
filename = directory + "/" + s[2];
163
ifstream infile(filename.c_str());
165
vector<string> strseq;
167
while(getline(infile,line))
168
strseq.push_back(line);
170
id.instrument.loadFromList(strseq);
171
if(id.instrument.name() != "unknown")
177
arts_warning("mapped instrument: "
178
"can't load structure %s",s[2].c_str());
184
InstrumentData::Range range;
185
range.minValue = atoi(s[2].c_str());
186
range.maxValue = range.minValue;
187
int i = s[2].find("-",0);
190
range.minValue = atoi(s[2].substr(0,i).c_str());
192
atoi(s[2].substr(i+1,s[2].size()-(i+1)).c_str());
194
if(s[0] == "pitch") id.pitch = range;
195
if(s[0] == "channel") id.channel = range;
196
if(s[0] == "program") id.program = range;
197
if(s[0] == "velocity") id.velocity = range;
204
if(loadOk) instruments.push_back(id);
207
void InstrumentMap::loadFromList(const string& filename,
208
const vector<string>& list)
210
int r = filename.rfind('/');
212
directory = filename.substr(0,r);
216
vector<string>::const_iterator i;
218
for(i = list.begin(); i != list.end(); i++) loadLine(*i);
221
StructureDesc InstrumentMap::getInstrument(mcopbyte channel, mcopbyte note,
222
mcopbyte velocity, mcopbyte program)
224
list<InstrumentData>::iterator i;
225
for(i = instruments.begin(); i != instruments.end(); i++)
227
InstrumentData &id = *i;
229
if(id.channel.match(channel) && id.pitch.match(note) &&
230
id.velocity.match(velocity) && id.program.match(program))
232
return id.instrument;
236
return StructureDesc::null();
240
/*-------instrument mapping end -------*/
242
static SynthModule get_AMAN_PLAY(Object structure)
244
Object resultObj = structure._getChild("play");
245
assert(!resultObj.isNull());
247
SynthModule result = DynamicCast(resultObj);
248
assert(!result.isNull());
256
TSNote(MidiPort port, const MidiEvent& event) :
257
port(port), event(event)
262
class AutoMidiRelease : public TimeNotify {
264
vector<MidiReleaseHelper> impls;
267
Dispatcher::the()->ioManager()->addTimer(10, this);
271
Dispatcher::the()->ioManager()->removeTimer(this);
275
vector<MidiReleaseHelper>::iterator i = impls.begin();
276
while(i != impls.end())
280
MidiReleaseHelper& helper = *i;
282
arts_debug("one voice terminated");
283
// put the MidiReleaseHelper and the voice into the ObjectCache
284
// (instead of simply freeing it)
285
ObjectCache cache = helper.cache();
286
SynthModule voice = helper.voice();
287
get_AMAN_PLAY(voice).stop();
289
cache.put(voice,helper.name());
297
// cache startup & shutdown
298
static class AutoMidiReleaseStart :public StartupClass
301
void startup() { autoMidiRelease = new AutoMidiRelease(); }
302
void shutdown() { delete autoMidiRelease; }
303
} autoMidiReleaseStart;
305
class MidiReleaseHelper_impl : virtual public MidiReleaseHelper_skel,
306
virtual public StdSynthModule
315
MidiReleaseHelper_impl()
317
autoMidiRelease->impls.push_back(MidiReleaseHelper::_from_base(_copy()));
319
~MidiReleaseHelper_impl() {
320
artsdebug("MidiReleaseHelper: one voice is gone now\n");
324
SynthModule voice() { return _voice; }
325
void voice(SynthModule voice) { _voice = voice; }
327
ObjectCache cache() { return _cache; }
328
void cache(ObjectCache cache) { _cache = cache; }
330
string name() { return _name; }
331
void name(const string& name) { _name = name; }
333
bool terminate() { return _terminate; }
334
void streamStart() { _terminate = false; }
336
void calculateBlock(unsigned long samples)
342
REGISTER_IMPLEMENTATION(MidiReleaseHelper_impl);
344
class Synth_MIDI_TEST_impl : virtual public Synth_MIDI_TEST_skel,
345
virtual public StdSynthModule {
348
SynthModule voice[128];
350
float pitchShiftValue;
353
// initialize all voices with NULL objects (no lazy create)
354
for(int i = 0; i < 128; i++) voice[i] = SynthModule::null();
356
pitchShiftValue = 0.0;
359
} *channelData; /* data for all 16 midi channels */
363
StructureDesc instrument;
364
StructureBuilder builder;
365
AudioManagerClient amClient;
373
Synth_MIDI_TEST self() { return Synth_MIDI_TEST::_from_base(_copy()); }
375
Synth_MIDI_TEST_impl();
376
~Synth_MIDI_TEST_impl();
378
void filename(const string& newname);
387
void noteOn(mcopbyte channel, mcopbyte note, mcopbyte velocity);
388
void noteOff(mcopbyte channel, mcopbyte note);
389
void pitchWheel(mcopbyte channel, mcopbyte lsb, mcopbyte msb);
391
float getFrequency(mcopbyte note,mcopbyte channel);
400
void processEvent(const MidiEvent& event)
402
timer.queueEvent(self(),event);
404
void processCommand(const MidiCommand& command)
406
mcopbyte channel = command.status & mcsChannelMask;
408
switch(command.status & mcsCommandMask)
410
case mcsNoteOn: noteOn(channel,command.data1,command.data2);
412
case mcsNoteOff: noteOff(channel,command.data1);
414
case mcsPitchWheel: pitchWheel(channel,command.data1,command.data2);
416
case mcsProgram: channelData[channel].program = command.data1;
419
if(command.data1 == mcpAllNotesOff && command.data2 == 0)
420
for(mcopbyte note=0; note<128; note++)
421
noteOff(channel,note);
426
REGISTER_IMPLEMENTATION(Synth_MIDI_TEST_impl);
429
void Synth_MIDI_TEST_impl::filename(const string& newname)
431
ifstream infile(newname.c_str());
433
vector<string> strseq;
435
while(getline(infile,line))
436
strseq.push_back(line);
440
/* search extension */
441
string::const_reverse_iterator i;
443
bool extensionok = false;
445
for(i = newname.rbegin(); i != newname.rend() && !extensionok; i++)
450
extension.insert(extension.begin(), (char)tolower(*i));
453
if(extensionok && extension == "arts")
455
instrument.loadFromList(strseq);
456
_title = "aRts Instrument ("+instrument.name()+")";
459
else if(extensionok && extension == "arts-map")
461
map.loadFromList(newname, strseq);
462
_title = "aRts Instrument (mapped)";
467
client.title(title());
468
amClient.title(title());
471
Synth_MIDI_TEST_impl::Synth_MIDI_TEST_impl()
472
: amClient(amPlay, "aRts Instrument","Synth_MIDI_TEST")
475
client = MidiClient::null();
476
timer = SubClass("Arts::AudioMidiTimer");
477
channelData = new ChannelData[16];
480
Synth_MIDI_TEST_impl::~Synth_MIDI_TEST_impl()
482
delete[] channelData;
485
void Synth_MIDI_TEST_impl::streamStart()
487
// register with the midi manager
488
MidiManager manager = Reference("global:Arts_MidiManager");
489
if(!manager.isNull())
491
client = manager.addClient(mcdRecord,mctDestination,title(),
492
"Arts::Synth_MIDI_TEST");
493
client.addInputPort(self());
496
arts_warning("Synth_MIDI_TEST: no midi manager found - not registered");
499
void Synth_MIDI_TEST_impl::streamEnd()
501
client = MidiClient::null();
504
void Synth_MIDI_TEST_impl::noteOn(mcopbyte channel, mcopbyte note,
509
noteOff(channel,note);
512
if(!channelData[channel].voice[note].isNull())
514
noteOff(channel,note);
515
arts_info("Synth_MIDI_TEST: duplicate noteOn (mixed channels?)");
520
mcopbyte program = channelData[channel].program;
521
StructureDesc sd = map.getInstrument(channel,note,velocity,program);
522
if(sd.isNull()) return;
526
Object structureObject = cache.get(instrument.name());
527
if(structureObject.isNull())
529
arts_debug("creating new structure");
530
structureObject = builder.createObject(instrument);
532
Synth_AMAN_PLAY play(amClient);
533
structureObject._addChild(play,"play");
534
connect(structureObject,"left",play,"left");
535
connect(structureObject,"right",play,"right");
539
arts_debug("used cached structure");
542
SynthModule structure = DynamicCast(structureObject);
543
assert(!structure.isNull());
545
setValue(structure,"frequency",getFrequency(note,channel));
546
setValue(structure,"velocity",(float)velocity/127.0);
547
setValue(structure,"pressed",1.0);
549
get_AMAN_PLAY(structure).start();
552
channelData[channel].voice[note] = structure;
553
channelData[channel].name[note] = instrument.name();
556
void Synth_MIDI_TEST_impl::noteOff(mcopbyte channel, mcopbyte note)
558
if(!channelData[channel].voice[note].isNull())
560
setValue(channelData[channel].voice[note],"pressed",0.0);
564
h.voice(channelData[channel].voice[note]);
566
h.name(channelData[channel].name[note]);
568
connect(channelData[channel].voice[note],"done",h,"done");
570
assert(!h.terminate());
571
channelData[channel].voice[note] = SynthModule::null();
575
float Synth_MIDI_TEST_impl::getFrequency(mcopbyte note, mcopbyte channel)
577
/* 2 semitones pitchshift */
578
return 261.63 * pow(2,((float)(note)+(channelData[channel].pitchShiftValue*2.0))/12.0)/32.0;
581
void Synth_MIDI_TEST_impl::pitchWheel(mcopbyte channel,
582
mcopbyte lsb, mcopbyte msb)
586
channelData[channel].pitchShiftValue =
587
(float)((lsb + msb*128) - (0x40*128))/8192.0;
589
for(note = 0; note < 128; note++)
591
if(!channelData[channel].voice[note].isNull())
592
setValue(channelData[channel].voice[note],"frequency",getFrequency(note,channel));