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.
545 lines
18 KiB
545 lines
18 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 <tqdatastream.h>
|
|
|
|
#include "Composition.h" // for RealTime
|
|
#include "Event.h"
|
|
|
|
|
|
#ifndef _MAPPEDEVENT_H_
|
|
#define _MAPPEDEVENT_H_
|
|
|
|
// Used as a transformation stage between Composition, Events and output
|
|
// at the Sequencer this class and MidiComposition eliminate the notion
|
|
// of the Segment and Track for ease of Event access. The MappedEvents
|
|
// are ready for playing or routing through an Instrument or Effects
|
|
// boxes.
|
|
//
|
|
// MappedEvents can also represent instructions for playback of audio
|
|
// samples - if the m_type is Audio then the sequencer will attempt to
|
|
// map the Pitch (m_data1) to the audio id. Note that this limits us
|
|
// to 256 audio files in the Composition unless we use a different
|
|
// parameter for storing these IDs.
|
|
//
|
|
// The MappedEvent/Instrument relationship is interesting - we don't
|
|
// want to duplicate the entire Instrument at the Sequencer level as
|
|
// it'd be messy and unnecessary. Instead we use a MappedInstrument
|
|
// which is just a very cut down Sequencer-side version of an Instrument.
|
|
//
|
|
// Some of these Events are unidirectional, some are bidirectional -
|
|
// that is they only have a meaning in one direction (they are still
|
|
// legal at either end). They are broadcast in both directions using
|
|
// the "getSequencerSlice" and "processAsync/Recorded" interfaces on
|
|
// which the control messages can piggyback and eventually stripped out.
|
|
//
|
|
|
|
namespace Rosegarden
|
|
{
|
|
class MappedEvent;
|
|
|
|
class DataBlockRepository
|
|
{
|
|
public:
|
|
friend class MappedEvent;
|
|
typedef unsigned long blockid;
|
|
|
|
static DataBlockRepository* getInstance();
|
|
static std::string getDataBlockForEvent(MappedEvent*);
|
|
static void setDataBlockForEvent(MappedEvent*, const std::string&);
|
|
/**
|
|
* Clear all block files
|
|
*/
|
|
static void clear();
|
|
bool hasDataBlock(blockid);
|
|
|
|
protected:
|
|
DataBlockRepository();
|
|
|
|
std::string getDataBlock(blockid);
|
|
|
|
void addDataByteForEvent(MidiByte byte, MappedEvent*);
|
|
void addDataStringForEvent(const std::string&, MappedEvent*);
|
|
|
|
|
|
blockid registerDataBlock(const std::string&);
|
|
void unregisterDataBlock(blockid);
|
|
|
|
void registerDataBlockForEvent(const std::string&, MappedEvent*);
|
|
void unregisterDataBlockForEvent(MappedEvent*);
|
|
|
|
|
|
//--------------- Data members ---------------------------------
|
|
|
|
static DataBlockRepository* m_instance;
|
|
};
|
|
|
|
|
|
class MappedEvent
|
|
{
|
|
public:
|
|
typedef enum
|
|
{
|
|
// INVALID
|
|
//
|
|
InvalidMappedEvent = 0,
|
|
|
|
// Keep the MidiNotes bit flaggable so that filtering works
|
|
//
|
|
MidiNote = 1 << 0,
|
|
MidiNoteOneShot = 1 << 1, // doesn't need NOTE OFFs
|
|
MidiProgramChange = 1 << 2,
|
|
MidiKeyPressure = 1 << 3,
|
|
MidiChannelPressure = 1 << 4,
|
|
MidiPitchBend = 1 << 5,
|
|
MidiController = 1 << 6,
|
|
MidiSystemMessage = 1 << 7,
|
|
|
|
// Sent from the gui to play an audio file
|
|
Audio = 1 << 8,
|
|
// Sent from gui to cancel playing an audio file
|
|
AudioCancel = 1 << 9,
|
|
// Sent to the gui with audio level on Instrument
|
|
AudioLevel = 1 << 10,
|
|
// Sent to the gui to inform an audio file stopped
|
|
AudioStopped = 1 << 11,
|
|
// The gui is clear to generate a preview for a new audio file
|
|
AudioGeneratePreview = 1 << 12,
|
|
|
|
// Update Instruments - new ALSA client detected
|
|
SystemUpdateInstruments = 1 << 13,
|
|
// Set RG as JACK master/slave
|
|
SystemJackTransport = 1 << 14,
|
|
// Set RG as MMC master/slave
|
|
SystemMMCTransport = 1 << 15,
|
|
// Set System Messages and MIDI Clock
|
|
SystemMIDIClock = 1 << 16,
|
|
// Set Record device
|
|
SystemRecordDevice = 1 << 17,
|
|
// Set Metronome device
|
|
SystemMetronomeDevice = 1 << 18,
|
|
// Set Audio inputs/outputs: data1 num inputs, data2 num submasters
|
|
SystemAudioPortCounts = 1 << 19,
|
|
// Set whether we create various Audio ports (data1 is an AudioOutMask)
|
|
SystemAudioPorts = 1 << 20,
|
|
// Some failure has occurred: data1 contains FailureCode
|
|
SystemFailure = 1 << 21,
|
|
|
|
// Time sig. event (from time sig. composition reference segment)
|
|
TimeSignature = 1 << 22,
|
|
// Tempo event (from tempo composition reference segment)
|
|
Tempo = 1 << 23,
|
|
|
|
// Panic function
|
|
Panic = 1 << 24,
|
|
|
|
// Set RG as MTC master/slave
|
|
SystemMTCTransport = 1 << 25,
|
|
// Auto-connect sync outputs
|
|
SystemMIDISyncAuto = 1 << 26,
|
|
// File format used for audio recording (data1 is 0=PCM,1=float)
|
|
SystemAudioFileFormat = 1 << 27
|
|
|
|
} MappedEventType;
|
|
|
|
typedef enum
|
|
{
|
|
// These values are OR'd to produce the data2 field in a
|
|
// SystemAudioPorts event.
|
|
FaderOuts = 1 << 0,
|
|
SubmasterOuts = 1 << 1
|
|
|
|
} MappedEventAudioOutMask;
|
|
|
|
typedef enum
|
|
{
|
|
// JACK is having some xruns - warn the user maybe
|
|
FailureXRuns = 0,
|
|
// JACK has died or kicked us out
|
|
FailureJackDied = 1,
|
|
// Audio subsystem failed to read from disc fast enough
|
|
FailureDiscUnderrun = 2,
|
|
// Audio subsystem failed to write to disc fast enough
|
|
FailureDiscOverrun = 3,
|
|
// Audio subsystem failed to mix busses fast enough
|
|
FailureBussMixUnderrun = 4,
|
|
// Audio subsystem failed to mix instruments fast enough
|
|
FailureMixUnderrun = 5,
|
|
// Using a timer that has too low a resolution (e.g. 100Hz system timer)
|
|
WarningImpreciseTimer = 6,
|
|
// Too much CPU time spent in audio processing -- risk of xruns and lockup
|
|
FailureCPUOverload = 7,
|
|
// JACK kicked us out, but we've reconnected
|
|
FailureJackRestart = 8,
|
|
// JACK kicked us out, and now the reconnection has failed
|
|
FailureJackRestartFailed = 9,
|
|
// A necessary ALSA call has returned an error code
|
|
FailureALSACallFailed = 10,
|
|
// Using a timer that has too low a resolution, but RTC might work
|
|
WarningImpreciseTimerTryRTC = 11,
|
|
} FailureCode;
|
|
|
|
MappedEvent(): m_trackId(0),
|
|
m_instrument(0),
|
|
m_type(MidiNote),
|
|
m_data1(0),
|
|
m_data2(0),
|
|
m_eventTime(0, 0),
|
|
m_duration(0, 0),
|
|
m_audioStartMarker(0, 0),
|
|
m_dataBlockId(0),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(-1),
|
|
m_autoFade(false),
|
|
m_fadeInTime(RealTime::zeroTime),
|
|
m_fadeOutTime(RealTime::zeroTime),
|
|
m_recordedChannel(0),
|
|
m_recordedDevice(0) {}
|
|
|
|
// Construct from Events to Internal (MIDI) type MappedEvent
|
|
//
|
|
MappedEvent(const Event &e);
|
|
|
|
// Another Internal constructor from Events
|
|
MappedEvent(InstrumentId id,
|
|
const Event &e,
|
|
const RealTime &eventTime,
|
|
const RealTime &duration);
|
|
|
|
// A general MappedEvent constructor for any MappedEvent type
|
|
//
|
|
MappedEvent(InstrumentId id,
|
|
MappedEventType type,
|
|
MidiByte pitch,
|
|
MidiByte velocity,
|
|
const RealTime &absTime,
|
|
const RealTime &duration,
|
|
const RealTime &audioStartMarker):
|
|
m_trackId(0),
|
|
m_instrument(id),
|
|
m_type(type),
|
|
m_data1(pitch),
|
|
m_data2(velocity),
|
|
m_eventTime(absTime),
|
|
m_duration(duration),
|
|
m_audioStartMarker(audioStartMarker),
|
|
m_dataBlockId(0),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(-1),
|
|
m_autoFade(false),
|
|
m_fadeInTime(RealTime::zeroTime),
|
|
m_fadeOutTime(RealTime::zeroTime),
|
|
m_recordedChannel(0),
|
|
m_recordedDevice(0) {}
|
|
|
|
// Audio MappedEvent shortcut constructor
|
|
//
|
|
MappedEvent(InstrumentId id,
|
|
unsigned short audioID,
|
|
const RealTime &eventTime,
|
|
const RealTime &duration,
|
|
const RealTime &audioStartMarker):
|
|
m_trackId(0),
|
|
m_instrument(id),
|
|
m_type(Audio),
|
|
m_data1(audioID % 256),
|
|
m_data2(audioID / 256),
|
|
m_eventTime(eventTime),
|
|
m_duration(duration),
|
|
m_audioStartMarker(audioStartMarker),
|
|
m_dataBlockId(0),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(-1),
|
|
m_autoFade(false),
|
|
m_fadeInTime(RealTime::zeroTime),
|
|
m_fadeOutTime(RealTime::zeroTime),
|
|
m_recordedChannel(0),
|
|
m_recordedDevice(0) {}
|
|
|
|
// More generalised MIDI event containers for
|
|
// large and small events (one param, two param)
|
|
//
|
|
MappedEvent(InstrumentId id,
|
|
MappedEventType type,
|
|
MidiByte data1,
|
|
MidiByte data2):
|
|
m_trackId(0),
|
|
m_instrument(id),
|
|
m_type(type),
|
|
m_data1(data1),
|
|
m_data2(data2),
|
|
m_eventTime(RealTime(0, 0)),
|
|
m_duration(RealTime(0, 0)),
|
|
m_audioStartMarker(RealTime(0, 0)),
|
|
m_dataBlockId(0),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(-1),
|
|
m_autoFade(false),
|
|
m_fadeInTime(RealTime::zeroTime),
|
|
m_fadeOutTime(RealTime::zeroTime),
|
|
m_recordedChannel(0),
|
|
m_recordedDevice(0) {}
|
|
|
|
MappedEvent(InstrumentId id,
|
|
MappedEventType type,
|
|
MidiByte data1):
|
|
m_trackId(0),
|
|
m_instrument(id),
|
|
m_type(type),
|
|
m_data1(data1),
|
|
m_data2(0),
|
|
m_eventTime(RealTime(0, 0)),
|
|
m_duration(RealTime(0, 0)),
|
|
m_audioStartMarker(RealTime(0, 0)),
|
|
m_dataBlockId(0),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(-1),
|
|
m_autoFade(false),
|
|
m_fadeInTime(RealTime::zeroTime),
|
|
m_fadeOutTime(RealTime::zeroTime),
|
|
m_recordedChannel(0),
|
|
m_recordedDevice(0) {}
|
|
|
|
|
|
// Construct SysExs say
|
|
//
|
|
MappedEvent(InstrumentId id,
|
|
MappedEventType type):
|
|
m_trackId(0),
|
|
m_instrument(id),
|
|
m_type(type),
|
|
m_data1(0),
|
|
m_data2(0),
|
|
m_eventTime(RealTime(0, 0)),
|
|
m_duration(RealTime(0, 0)),
|
|
m_audioStartMarker(RealTime(0, 0)),
|
|
m_dataBlockId(0),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(-1),
|
|
m_autoFade(false),
|
|
m_fadeInTime(RealTime::zeroTime),
|
|
m_fadeOutTime(RealTime::zeroTime),
|
|
m_recordedChannel(0),
|
|
m_recordedDevice(0) {}
|
|
|
|
// Copy constructor
|
|
//
|
|
// Fix for 674731 by Pedro Lopez-Cabanillas (20030531)
|
|
MappedEvent(const MappedEvent &mE):
|
|
m_trackId(mE.getTrackId()),
|
|
m_instrument(mE.getInstrument()),
|
|
m_type(mE.getType()),
|
|
m_data1(mE.getData1()),
|
|
m_data2(mE.getData2()),
|
|
m_eventTime(mE.getEventTime()),
|
|
m_duration(mE.getDuration()),
|
|
m_audioStartMarker(mE.getAudioStartMarker()),
|
|
m_dataBlockId(mE.getDataBlockId()),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(mE.getRuntimeSegmentId()),
|
|
m_autoFade(mE.isAutoFading()),
|
|
m_fadeInTime(mE.getFadeInTime()),
|
|
m_fadeOutTime(mE.getFadeOutTime()),
|
|
m_recordedChannel(mE.getRecordedChannel()),
|
|
m_recordedDevice(mE.getRecordedDevice()) {}
|
|
|
|
// Copy from pointer
|
|
// Fix for 674731 by Pedro Lopez-Cabanillas (20030531)
|
|
MappedEvent(MappedEvent *mE):
|
|
m_trackId(mE->getTrackId()),
|
|
m_instrument(mE->getInstrument()),
|
|
m_type(mE->getType()),
|
|
m_data1(mE->getData1()),
|
|
m_data2(mE->getData2()),
|
|
m_eventTime(mE->getEventTime()),
|
|
m_duration(mE->getDuration()),
|
|
m_audioStartMarker(mE->getAudioStartMarker()),
|
|
m_dataBlockId(mE->getDataBlockId()),
|
|
m_isPersistent(false),
|
|
m_runtimeSegmentId(mE->getRuntimeSegmentId()),
|
|
m_autoFade(mE->isAutoFading()),
|
|
m_fadeInTime(mE->getFadeInTime()),
|
|
m_fadeOutTime(mE->getFadeOutTime()),
|
|
m_recordedChannel(mE->getRecordedChannel()),
|
|
m_recordedDevice(mE->getRecordedDevice()) {}
|
|
|
|
// Construct perhaps without initialising, for placement new or equivalent
|
|
MappedEvent(bool initialise) {
|
|
if (initialise) *this = MappedEvent();
|
|
}
|
|
|
|
// Event time
|
|
//
|
|
void setEventTime(const RealTime &a) { m_eventTime = a; }
|
|
RealTime getEventTime() const { return m_eventTime; }
|
|
|
|
// Duration
|
|
//
|
|
void setDuration(const RealTime &d) { m_duration = d; }
|
|
RealTime getDuration() const { return m_duration; }
|
|
|
|
// Instrument
|
|
void setInstrument(InstrumentId id) { m_instrument = id; }
|
|
InstrumentId getInstrument() const { return m_instrument; }
|
|
|
|
// Track
|
|
void setTrackId(TrackId id) { m_trackId = id; }
|
|
TrackId getTrackId() const { return m_trackId; }
|
|
|
|
MidiByte getPitch() const { return m_data1; }
|
|
|
|
// Keep pitch within MIDI limits
|
|
//
|
|
void setPitch(MidiByte p)
|
|
{
|
|
m_data1 = p;
|
|
if (m_data1 > MidiMaxValue) m_data1 = MidiMaxValue;
|
|
}
|
|
|
|
void setVelocity(MidiByte v) { m_data2 = v; }
|
|
MidiByte getVelocity() const { return m_data2; }
|
|
|
|
// And the trendy names for them
|
|
//
|
|
MidiByte getData1() const { return m_data1; }
|
|
MidiByte getData2() const { return m_data2; }
|
|
void setData1(MidiByte d1) { m_data1 = d1; }
|
|
void setData2(MidiByte d2) { m_data2 = d2; }
|
|
|
|
void setAudioID(unsigned short id) { m_data1 = id % 256; m_data2 = id / 256; }
|
|
int getAudioID() const { return m_data1 + 256 * m_data2; }
|
|
|
|
// A sample doesn't have to be played from the beginning. When
|
|
// passing an Audio event this value may be set to indicate from
|
|
// where in the sample it should be played. Duration is measured
|
|
// against total sounding length (not absolute position).
|
|
//
|
|
void setAudioStartMarker(const RealTime &aS)
|
|
{ m_audioStartMarker = aS; }
|
|
RealTime getAudioStartMarker() const
|
|
{ return m_audioStartMarker; }
|
|
|
|
MappedEventType getType() const { return m_type; }
|
|
void setType(const MappedEventType &value) { m_type = value; }
|
|
|
|
// Data block id
|
|
//
|
|
DataBlockRepository::blockid getDataBlockId() const { return m_dataBlockId; }
|
|
void setDataBlockId(DataBlockRepository::blockid dataBlockId) { m_dataBlockId = dataBlockId; }
|
|
|
|
// How MappedEvents are ordered in the MappedComposition
|
|
//
|
|
struct MappedEventCmp
|
|
{
|
|
bool operator()(const MappedEvent *mE1, const MappedEvent *mE2) const
|
|
{
|
|
return *mE1 < *mE2;
|
|
}
|
|
};
|
|
|
|
friend bool operator<(const MappedEvent &a, const MappedEvent &b);
|
|
|
|
MappedEvent& operator=(const MappedEvent &mE);
|
|
|
|
friend TQDataStream& operator>>(TQDataStream &dS, MappedEvent *mE);
|
|
friend TQDataStream& operator<<(TQDataStream &dS, MappedEvent *mE);
|
|
friend TQDataStream& operator>>(TQDataStream &dS, MappedEvent &mE);
|
|
friend TQDataStream& operator<<(TQDataStream &dS, const MappedEvent &mE);
|
|
|
|
/// Add a single byte to the event's datablock (for SysExs)
|
|
void addDataByte(MidiByte byte);
|
|
/// Add several bytes to the event's datablock
|
|
void addDataString(const std::string& data);
|
|
|
|
void setPersistent(bool value) { m_isPersistent = value; }
|
|
bool isPersistent() const { return m_isPersistent; }
|
|
|
|
/// Size of a MappedEvent in a stream
|
|
static const size_t streamedSize;
|
|
|
|
// The runtime segment id of an audio file
|
|
//
|
|
int getRuntimeSegmentId() const { return m_runtimeSegmentId; }
|
|
void setRuntimeSegmentId(int id) { m_runtimeSegmentId = id; }
|
|
|
|
bool isAutoFading() const { return m_autoFade; }
|
|
void setAutoFade(bool value) { m_autoFade = value; }
|
|
|
|
RealTime getFadeInTime() const { return m_fadeInTime; }
|
|
void setFadeInTime(const RealTime &time)
|
|
{ m_fadeInTime = time; }
|
|
|
|
RealTime getFadeOutTime() const { return m_fadeOutTime; }
|
|
void setFadeOutTime(const RealTime &time)
|
|
{ m_fadeOutTime = time; }
|
|
|
|
// Original event input channel as it was recorded
|
|
//
|
|
unsigned int getRecordedChannel() const { return m_recordedChannel; }
|
|
void setRecordedChannel(const unsigned int channel)
|
|
{ m_recordedChannel = channel; }
|
|
|
|
// Original event record device as it was recorded
|
|
//
|
|
unsigned int getRecordedDevice() const { return m_recordedDevice; }
|
|
void setRecordedDevice(const unsigned int device) { m_recordedDevice = device; }
|
|
|
|
private:
|
|
TrackId m_trackId;
|
|
InstrumentId m_instrument;
|
|
MappedEventType m_type;
|
|
MidiByte m_data1;
|
|
MidiByte m_data2;
|
|
RealTime m_eventTime;
|
|
RealTime m_duration;
|
|
RealTime m_audioStartMarker;
|
|
|
|
// Use this when we want to store something in addition to the
|
|
// other bytes in this type, e.g. System Exclusive.
|
|
//
|
|
DataBlockRepository::blockid m_dataBlockId;
|
|
|
|
// Should a MappedComposition try and delete this MappedEvent or
|
|
// if it persistent?
|
|
//
|
|
bool m_isPersistent;
|
|
|
|
|
|
// Id of the segment that this (audio) event is derived from
|
|
//
|
|
int m_runtimeSegmentId;
|
|
|
|
// Audio autofading
|
|
//
|
|
bool m_autoFade;
|
|
RealTime m_fadeInTime;
|
|
RealTime m_fadeOutTime;
|
|
|
|
// input event original data,
|
|
// stored as it was recorded
|
|
//
|
|
unsigned int m_recordedChannel;
|
|
unsigned int m_recordedDevice;
|
|
};
|
|
|
|
|
|
}
|
|
|
|
#endif
|