You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/base/Instrument.cpp

638 lines
15 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
The moral right of the authors to claim authorship of this work
has been asserted.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include <stdio.h>
#include <sstream>
#include "Instrument.h"
#include "MidiDevice.h"
#include "AudioPluginInstance.h"
#include "AudioLevel.h"
namespace Rosegarden
{
const unsigned int PluginContainer::PLUGIN_COUNT = 5;
PluginContainer::PluginContainer(bool havePlugins)
{
if (havePlugins) {
// Add a number of plugin place holders (unassigned)
for (unsigned int i = 0; i < PLUGIN_COUNT; i++)
addPlugin(new AudioPluginInstance(i));
}
}
PluginContainer::~PluginContainer()
{
clearPlugins();
}
void
PluginContainer::addPlugin(AudioPluginInstance *instance)
{
m_audioPlugins.push_back(instance);
}
bool
PluginContainer::removePlugin(unsigned int position)
{
PluginInstanceIterator it = m_audioPlugins.begin();
for (; it != m_audioPlugins.end(); it++)
{
if ((*it)->getPosition() == position)
{
delete (*it);
m_audioPlugins.erase(it);
return true;
}
}
return false;
}
void
PluginContainer::clearPlugins()
{
PluginInstanceIterator it = m_audioPlugins.begin();
for (; it != m_audioPlugins.end(); it++)
delete (*it);
m_audioPlugins.erase(m_audioPlugins.begin(), m_audioPlugins.end());
}
void
PluginContainer::emptyPlugins()
{
PluginInstanceIterator it = m_audioPlugins.begin();
for (; it != m_audioPlugins.end(); it++)
{
(*it)->setAssigned(false);
(*it)->setBypass(false);
(*it)->clearPorts();
}
}
// Get an instance for an index
//
AudioPluginInstance*
PluginContainer::getPlugin(unsigned int position)
{
PluginInstanceIterator it = m_audioPlugins.begin();
for (; it != m_audioPlugins.end(); it++)
{
if ((*it)->getPosition() == position)
return *it;
}
return 0;
}
const unsigned int Instrument::SYNTH_PLUGIN_POSITION = 999;
Instrument::Instrument(InstrumentId id,
InstrumentType it,
const std::string &name,
Device *device):
PluginContainer(it == Audio || it == SoftSynth),
m_id(id),
m_name(name),
m_type(it),
m_channel(0),
//m_input_channel(-1),
m_transpose(MidiMidValue),
m_pan(MidiMidValue),
m_volume(100),
m_level(0.0),
m_recordLevel(0.0),
m_device(device),
m_sendBankSelect(false),
m_sendProgramChange(false),
m_sendPan(false),
m_sendVolume(false),
m_mappedId(0),
m_audioInput(1000),
m_audioInputChannel(0),
m_audioOutput(0)
{
if (it == Audio || it == SoftSynth)
{
// In an audio instrument we use the m_channel attribute to
// hold the number of audio channels this Instrument uses -
// not the MIDI channel number. Default is 2 (stereo).
//
m_channel = 2;
m_pan = 100; // audio pan ranges from -100 to 100 but
// we store within an unsigned char as
// 0 to 200.
}
if (it == SoftSynth) {
addPlugin(new AudioPluginInstance(SYNTH_PLUGIN_POSITION));
}
}
Instrument::Instrument(InstrumentId id,
InstrumentType it,
const std::string &name,
MidiByte channel,
Device *device):
PluginContainer(it == Audio || it == SoftSynth),
m_id(id),
m_name(name),
m_type(it),
m_channel(channel),
//m_input_channel(-1),
m_transpose(MidiMidValue),
m_pan(MidiMidValue),
m_volume(100),
m_level(0.0),
m_recordLevel(0.0),
m_device(device),
m_sendBankSelect(false),
m_sendProgramChange(false),
m_sendPan(false),
m_sendVolume(false),
m_mappedId(0),
m_audioInput(1000),
m_audioInputChannel(0),
m_audioOutput(0)
{
// Add a number of plugin place holders (unassigned)
//
if (it == Audio || it == SoftSynth)
{
// In an audio instrument we use the m_channel attribute to
// hold the number of audio channels this Instrument uses -
// not the MIDI channel number. Default is 2 (stereo).
//
m_channel = 2;
m_pan = 100; // audio pan ranges from -100 to 100 but
// we store within an unsigned char as
} else {
/*
*
* Let's try getting rid of this default behavior, and replacing it with a
* change to the factory autoload instead, because this just doesn't work out
* very well, and it's fiddly trying to sort the overall behavior into something
* less quirky (dmm)
*
// Also defined in Midi.h but we don't use that - not here
// in the clean inner sanctum.
//
const MidiByte MIDI_PERCUSSION_CHANNEL = 9;
const MidiByte MIDI_EXTENDED_PERCUSSION_CHANNEL = 10;
if (m_channel == MIDI_PERCUSSION_CHANNEL ||
m_channel == MIDI_EXTENDED_PERCUSSION_CHANNEL) {
setPercussion(true);
}
*/
}
if (it == SoftSynth) {
addPlugin(new AudioPluginInstance(SYNTH_PLUGIN_POSITION));
}
}
Instrument::Instrument(const Instrument &ins):
XmlExportable(),
PluginContainer(ins.getType() == Audio || ins.getType() == SoftSynth),
m_id(ins.getId()),
m_name(ins.getName()),
m_type(ins.getType()),
m_channel(ins.getMidiChannel()),
//m_input_channel(ins.getMidiInputChannel()),
m_program(ins.getProgram()),
m_transpose(ins.getMidiTranspose()),
m_pan(ins.getPan()),
m_volume(ins.getVolume()),
m_level(ins.getLevel()),
m_recordLevel(ins.getRecordLevel()),
m_device(ins.getDevice()),
m_sendBankSelect(ins.sendsBankSelect()),
m_sendProgramChange(ins.sendsProgramChange()),
m_sendPan(ins.sendsPan()),
m_sendVolume(ins.sendsVolume()),
m_mappedId(ins.getMappedId()),
m_audioInput(ins.m_audioInput),
m_audioInputChannel(ins.m_audioInputChannel),
m_audioOutput(ins.m_audioOutput)
{
if (ins.getType() == Audio || ins.getType() == SoftSynth)
{
// In an audio instrument we use the m_channel attribute to
// hold the number of audio channels this Instrument uses -
// not the MIDI channel number. Default is 2 (stereo).
//
m_channel = 2;
}
if (ins.getType() == SoftSynth) {
addPlugin(new AudioPluginInstance(SYNTH_PLUGIN_POSITION));
}
}
Instrument &
Instrument::operator=(const Instrument &ins)
{
if (&ins == this) return *this;
m_id = ins.getId();
m_name = ins.getName();
m_type = ins.getType();
m_channel = ins.getMidiChannel();
//m_input_channel = ins.getMidiInputChannel();
m_program = ins.getProgram();
m_transpose = ins.getMidiTranspose();
m_pan = ins.getPan();
m_volume = ins.getVolume();
m_level = ins.getLevel();
m_recordLevel = ins.getRecordLevel();
m_device = ins.getDevice();
m_sendBankSelect = ins.sendsBankSelect();
m_sendProgramChange = ins.sendsProgramChange();
m_sendPan = ins.sendsPan();
m_sendVolume = ins.sendsVolume();
m_mappedId = ins.getMappedId();
m_audioInput = ins.m_audioInput;
m_audioInputChannel = ins.m_audioInputChannel;
m_audioOutput = ins.m_audioOutput;
return *this;
}
Instrument::~Instrument()
{
}
std::string
Instrument::getPresentationName() const
{
if (m_type == Audio || m_type == SoftSynth || !m_device) {
return m_name;
} else {
return m_device->getName() + " " + m_name;
}
}
void
Instrument::setProgramChange(MidiByte program)
{
m_program = MidiProgram(m_program.getBank(), program);
}
MidiByte
Instrument::getProgramChange() const
{
return m_program.getProgram();
}
void
Instrument::setMSB(MidiByte msb)
{
m_program = MidiProgram(MidiBank(m_program.getBank().isPercussion(),
msb,
m_program.getBank().getLSB()),
m_program.getProgram());
}
MidiByte
Instrument::getMSB() const
{
return m_program.getBank().getMSB();
}
void
Instrument::setLSB(MidiByte lsb)
{
m_program = MidiProgram(MidiBank(m_program.getBank().isPercussion(),
m_program.getBank().getMSB(),
lsb),
m_program.getProgram());
}
MidiByte
Instrument::getLSB() const
{
return m_program.getBank().getLSB();
}
void
Instrument::setPercussion(bool percussion)
{
m_program = MidiProgram(MidiBank(percussion,
m_program.getBank().getMSB(),
m_program.getBank().getLSB()),
m_program.getProgram());
}
bool
Instrument::isPercussion() const
{
return m_program.getBank().isPercussion();
}
void
Instrument::setAudioInputToBuss(BussId buss, int channel)
{
m_audioInput = buss;
m_audioInputChannel = channel;
}
void
Instrument::setAudioInputToRecord(int recordIn, int channel)
{
m_audioInput = recordIn + 1000;
m_audioInputChannel = channel;
}
int
Instrument::getAudioInput(bool &isBuss, int &channel) const
{
channel = m_audioInputChannel;
if (m_audioInput >= 1000) {
isBuss = false;
return m_audioInput - 1000;
} else {
isBuss = true;
return m_audioInput;
}
}
// Implementation of the virtual method to output this class
// as XML. We don't send out the name as it's redundant in
// the file - that is driven from the sequencer.
//
//
std::string
Instrument::toXmlString()
{
std::stringstream instrument;
// We don't send system Instruments out this way -
// only user Instruments.
//
if (m_id < AudioInstrumentBase)
{
#if (__GNUC__ < 3)
instrument << std::ends;
#endif
return instrument.str();
}
instrument << " <instrument id=\"" << m_id;
instrument << "\" channel=\"" << (int)m_channel;
instrument << "\" type=\"";
if (m_type == Midi)
{
instrument << "midi\">" << std::endl;
if (m_sendBankSelect)
{
instrument << " <bank percussion=\""
<< (isPercussion() ? "true" : "false") << "\" msb=\""
<< (int)getMSB();
instrument << "\" lsb=\"" << (int)getLSB() << "\"/>" << std::endl;
}
if (m_sendProgramChange)
{
instrument << " <program id=\""
<< (int)getProgramChange() << "\"/>"
<< std::endl;
}
instrument << " <pan value=\""
<< (int)m_pan << "\"/>" << std::endl;
instrument << " <volume value=\""
<< (int)m_volume << "\"/>" << std::endl;
for (StaticControllerConstIterator it = m_staticControllers.begin();
it != m_staticControllers.end(); ++it)
{
instrument << " <controlchange type=\"" << int(it->first)
<< "\" value=\"" << int(it->second) << "\"/>" << std::endl;
}
}
else // Audio or SoftSynth
{
if (m_type == Audio) {
instrument << "audio\">" << std::endl;
} else {
instrument << "softsynth\">" << std::endl;
}
instrument << " <pan value=\""
<< (int)m_pan << "\"/>" << std::endl;
instrument << " <level value=\""
<< m_level << "\"/>" << std::endl;
instrument << " <recordLevel value=\""
<< m_recordLevel << "\"/>" << std::endl;
bool aibuss;
int channel;
int ai = getAudioInput(aibuss, channel);
instrument << " <audioInput value=\""
<< ai << "\" type=\""
<< (aibuss ? "buss" : "record")
<< "\" channel=\"" << channel
<< "\"/>" << std::endl;
instrument << " <audioOutput value=\""
<< m_audioOutput << "\"/>" << std::endl;
PluginInstanceIterator it = m_audioPlugins.begin();
for (; it != m_audioPlugins.end(); it++)
{
instrument << (*it)->toXmlString();
}
}
instrument << " </instrument>" << std::endl
#if (__GNUC__ < 3)
<< std::endl << std::ends;
#else
<< std::endl;
#endif
return instrument.str();
}
// Return a program name given a bank select (and whether
// we send it or not)
//
std::string
Instrument::getProgramName() const
{
if (m_sendProgramChange == false)
return std::string("");
MidiProgram program(m_program);
if (!m_sendBankSelect)
program = MidiProgram(MidiBank(isPercussion(), 0, 0), program.getProgram());
return ((dynamic_cast<MidiDevice*>(m_device))->getProgramName(program));
}
void
Instrument::setControllerValue(MidiByte controller, MidiByte value)
{
for (StaticControllerIterator it = m_staticControllers.begin();
it != m_staticControllers.end(); ++it)
{
if (it->first == controller)
{
it->second = value;
return;
}
}
m_staticControllers.push_back(std::pair<MidiByte, MidiByte>(controller, value));
}
MidiByte
Instrument::getControllerValue(MidiByte controller) const
{
for (StaticControllerConstIterator it = m_staticControllers.begin();
it != m_staticControllers.end(); ++it)
{
if (it->first == controller)
return it->second;
}
throw std::string("<no controller of that value>");
}
const MidiKeyMapping *
Instrument::getKeyMapping() const
{
MidiDevice *md = dynamic_cast<MidiDevice*>(m_device);
if (!md) return 0;
const MidiKeyMapping *mkm = md->getKeyMappingForProgram(m_program);
if (mkm) return mkm;
if (isPercussion()) { // if any key mapping is available, use it
const KeyMappingList &kml = md->getKeyMappings();
if (kml.begin() != kml.end()) {
return &(*kml.begin());
}
}
return 0;
}
Buss::Buss(BussId id) :
PluginContainer(true),
m_id(id),
m_level(0.0),
m_pan(100),
m_mappedId(0)
{
}
Buss::~Buss()
{
}
std::string
Buss::toXmlString()
{
std::stringstream buss;
buss << " <buss id=\"" << m_id << "\">" << std::endl;
buss << " <pan value=\"" << (int)m_pan << "\"/>" << std::endl;
buss << " <level value=\"" << m_level << "\"/>" << std::endl;
PluginInstanceIterator it = m_audioPlugins.begin();
for (; it != m_audioPlugins.end(); it++) {
buss << (*it)->toXmlString();
}
buss << " </buss>" << std::endl;
#if (__GNUC__ < 3)
buss << std::ends;
#endif
return buss.str();
}
std::string
Buss::getName() const
{
char buffer[20];
sprintf(buffer, "Submaster %d", m_id);
return buffer;
}
std::string
Buss::getPresentationName() const
{
return getName();
}
RecordIn::RecordIn() :
m_mappedId(0)
{
}
RecordIn::~RecordIn()
{
}
std::string
RecordIn::toXmlString()
{
// We don't actually save these, as they have nothing persistent
// in them. The studio just remembers how many there should be.
return "";
}
}