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/sound/AlsaDriver.h

560 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.
*/
// Specialisation of SoundDriver to support ALSA (http://www.alsa-project.org)
//
//
#ifndef _ALSADRIVER_H_
#define _ALSADRIVER_H_
#include <vector>
#include <set>
#include <map>
#ifdef HAVE_ALSA
#include <alsa/asoundlib.h> // ALSA
#include "SoundDriver.h"
#include "Instrument.h"
#include "Device.h"
#include "AlsaPort.h"
#include "Scavenger.h"
#include "RunnablePluginInstance.h"
#ifdef HAVE_LIBJACK
#include "JackDriver.h"
#endif
namespace Rosegarden
{
class AlsaDriver : public SoundDriver
{
public:
AlsaDriver(MappedStudio *studio);
virtual ~AlsaDriver();
// shutdown everything that's currently open
void shutdown();
virtual bool initialise();
virtual void initialisePlayback(const RealTime &position);
virtual void stopPlayback();
virtual void punchOut();
virtual void resetPlayback(const RealTime &oldPosition, const RealTime &position);
virtual void allNotesOff();
virtual void processNotesOff(const RealTime &time, bool now, bool everything = false);
virtual RealTime getSequencerTime();
virtual MappedComposition *getMappedComposition();
virtual bool record(RecordStatus recordStatus,
const std::vector<InstrumentId> *armedInstruments = 0,
const std::vector<TQString> *audioFileNames = 0);
virtual void startClocks();
virtual void startClocksApproved(); // called by JACK driver in sync mode
virtual void stopClocks();
virtual bool areClocksRunning() const { return m_queueRunning; }
virtual void processEventsOut(const MappedComposition &mC);
virtual void processEventsOut(const MappedComposition &mC,
const RealTime &sliceStart,
const RealTime &sliceEnd);
// Return the sample rate
//
virtual unsigned int getSampleRate() const {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getSampleRate();
else return 0;
#else
return 0;
#endif
}
// Define here to catch this being reset
//
virtual void setMIDIClockInterval(RealTime interval);
// initialise subsystems
//
bool initialiseMidi();
void initialiseAudio();
// Some stuff to help us debug this
//
void getSystemInfo();
void showQueueStatus(int queue);
// Process pending
//
virtual void processPending();
// We can return audio control signals to the gui using MappedEvents.
// Meter levels or audio file completions can go in here.
//
void insertMappedEventForReturn(MappedEvent *mE);
virtual RealTime getAudioPlayLatency() {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getAudioPlayLatency();
#endif
return RealTime::zeroTime;
}
virtual RealTime getAudioRecordLatency() {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getAudioRecordLatency();
#endif
return RealTime::zeroTime;
}
virtual RealTime getInstrumentPlayLatency(InstrumentId id) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getInstrumentPlayLatency(id);
#endif
return RealTime::zeroTime;
}
virtual RealTime getMaximumPlayLatency() {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getMaximumPlayLatency();
#endif
return RealTime::zeroTime;
}
// Plugin instance management
//
virtual void setPluginInstance(InstrumentId id,
TQString identifier,
int position) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->setPluginInstance(id, identifier, position);
#endif
}
virtual void removePluginInstance(InstrumentId id, int position) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->removePluginInstance(id, position);
#endif
}
// Remove all plugin instances
//
virtual void removePluginInstances() {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->removePluginInstances();
#endif
}
virtual void setPluginInstancePortValue(InstrumentId id,
int position,
unsigned long portNumber,
float value) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->setPluginInstancePortValue(id, position, portNumber, value);
#endif
}
virtual float getPluginInstancePortValue(InstrumentId id,
int position,
unsigned long portNumber) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getPluginInstancePortValue(id, position, portNumber);
#endif
return 0;
}
virtual void setPluginInstanceBypass(InstrumentId id,
int position,
bool value) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->setPluginInstanceBypass(id, position, value);
#endif
}
virtual TQStringList getPluginInstancePrograms(InstrumentId id,
int position) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getPluginInstancePrograms(id, position);
#endif
return TQStringList();
}
virtual TQString getPluginInstanceProgram(InstrumentId id,
int position) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getPluginInstanceProgram(id, position);
#endif
return TQString();
}
virtual TQString getPluginInstanceProgram(InstrumentId id,
int position,
int bank,
int program) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getPluginInstanceProgram(id, position, bank, program);
#endif
return TQString();
}
virtual unsigned long getPluginInstanceProgram(InstrumentId id,
int position,
TQString name) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->getPluginInstanceProgram(id, position, name);
#endif
return 0;
}
virtual void setPluginInstanceProgram(InstrumentId id,
int position,
TQString program) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->setPluginInstanceProgram(id, position, program);
#endif
}
virtual TQString configurePlugin(InstrumentId id,
int position,
TQString key,
TQString value) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) return m_jackDriver->configurePlugin(id, position, key, value);
#endif
return TQString();
}
virtual void setAudioBussLevels(int bussId,
float dB,
float pan) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->setAudioBussLevels(bussId, dB, pan);
#endif
}
virtual void setAudioInstrumentLevels(InstrumentId instrument,
float dB,
float pan) {
#ifdef HAVE_LIBJACK
if (m_jackDriver) m_jackDriver->setAudioInstrumentLevels(instrument, dB, pan);
#endif
}
virtual void claimUnwantedPlugin(void *plugin);
virtual void scavengePlugins();
virtual bool checkForNewClients();
virtual void setLoop(const RealTime &loopStart, const RealTime &loopEnd);
virtual void sleep(const RealTime &);
// ----------------------- End of Virtuals ----------------------
// Create and send an MMC command
//
void sendMMC(MidiByte deviceId,
MidiByte instruction,
bool isCommand,
const std::string &data);
// Check whether the given event is an MMC command we need to act on
// (and if so act on it)
//
bool testForMMCSysex(const snd_seq_event_t *event);
// Create and enqueue a batch of MTC quarter-frame events
//
void insertMTCTQFrames(RealTime sliceStart, RealTime sliceEnd);
// Create and enqueue an MTC full-frame system exclusive event
//
void insertMTCFullFrame(RealTime time);
// Parse and accept an incoming MTC quarter-frame event
//
void handleMTCTQFrame(unsigned int data_byte, RealTime the_time);
// Check whether the given event is an MTC sysex we need to act on
// (and if so act on it)
//
bool testForMTCSysex(const snd_seq_event_t *event);
// Adjust the ALSA clock skew for MTC lock
//
void tweakSkewForMTC(int factor);
// Recalibrate internal MTC factors
//
void calibrateMTC();
// Send a System message straight away
//
void sendSystemDirect(MidiByte command, int *arg);
// Scheduled system message with arguments
//
void sendSystemQueued(MidiByte command,
const std::string &args,
const RealTime &time);
// Set the record device
//
void setRecordDevice(DeviceId id, bool connectAction);
void unsetRecordDevices();
virtual bool canReconnect(Device::DeviceType type);
virtual DeviceId addDevice(Device::DeviceType type,
MidiDevice::DeviceDirection direction);
virtual void removeDevice(DeviceId id);
virtual void renameDevice(DeviceId id, TQString name);
// Get available connections per device
//
virtual unsigned int getConnections(Device::DeviceType type,
MidiDevice::DeviceDirection direction);
virtual TQString getConnection(Device::DeviceType type,
MidiDevice::DeviceDirection direction,
unsigned int connectionNo);
virtual void setConnection(DeviceId deviceId, TQString connection);
virtual void setPlausibleConnection(DeviceId deviceId, TQString connection);
virtual unsigned int getTimers();
virtual TQString getTimer(unsigned int);
virtual TQString getCurrentTimer();
virtual void setCurrentTimer(TQString);
virtual void getAudioInstrumentNumbers(InstrumentId &audioInstrumentBase,
int &audioInstrumentCount) {
audioInstrumentBase = AudioInstrumentBase;
#ifdef HAVE_LIBJACK
audioInstrumentCount = AudioInstrumentCount;
#else
audioInstrumentCount = 0;
#endif
}
virtual void getSoftSynthInstrumentNumbers(InstrumentId &ssInstrumentBase,
int &ssInstrumentCount) {
ssInstrumentBase = SoftSynthInstrumentBase;
#ifdef HAVE_DSSI
ssInstrumentCount = SoftSynthInstrumentCount;
#else
ssInstrumentCount = 0;
#endif
}
virtual TQString getStatusLog();
// To be called regularly from JACK driver when idle
void checkTimerSync(size_t frames);
virtual void runTasks();
// Report a failure back to the GUI
//
virtual void reportFailure(MappedEvent::FailureCode code);
protected:
typedef std::vector<AlsaPortDescription *> AlsaPortList;
ClientPortPair getFirstDestination(bool duplex);
ClientPortPair getPairForMappedInstrument(InstrumentId id);
int getOutputPortForMappedInstrument(InstrumentId id);
std::map<unsigned int, std::map<unsigned int, MappedEvent*> > m_noteOnMap;
/**
* Bring m_alsaPorts up-to-date; if newPorts is non-null, also
* return the new ports (not previously in m_alsaPorts) through it
*/
virtual void generatePortList(AlsaPortList *newPorts = 0);
virtual void generateInstruments();
virtual void generateTimerList();
virtual std::string getAutoTimer(bool &wantTimerChecks);
void addInstrumentsForDevice(MappedDevice *device);
MappedDevice *createMidiDevice(AlsaPortDescription *,
MidiDevice::DeviceDirection);
virtual void processMidiOut(const MappedComposition &mC,
const RealTime &sliceStart,
const RealTime &sliceEnd);
virtual void processSoftSynthEventOut(InstrumentId id,
const snd_seq_event_t *event,
bool now);
virtual bool isRecording(AlsaPortDescription *port);
virtual void processAudioQueue(bool /* now */) { }
virtual void setConnectionToDevice(MappedDevice &device, TQString connection);
virtual void setConnectionToDevice(MappedDevice &device, TQString connection,
const ClientPortPair &pair);
private:
RealTime getAlsaTime();
// Locally convenient to control our devices
//
void sendDeviceController(DeviceId device,
MidiByte byte1,
MidiByte byte2);
int checkAlsaError(int rc, const char *message);
AlsaPortList m_alsaPorts;
// ALSA MIDI/Sequencer stuff
//
snd_seq_t *m_midiHandle;
int m_client;
int m_inputPort;
typedef std::map<DeviceId, int> DeviceIntMap;
DeviceIntMap m_outputPorts;
int m_syncOutputPort;
int m_controllerPort;
int m_queue;
int m_maxClients;
int m_maxPorts;
int m_maxQueues;
// Because this can fail even if the driver's up (if
// another service is using the port say)
//
bool m_midiInputPortConnected;
bool m_midiSyncAutoConnect;
RealTime m_alsaPlayStartTime;
RealTime m_alsaRecordStartTime;
RealTime m_loopStartTime;
RealTime m_loopEndTime;
// MIDI Time Code handling:
unsigned int m_eat_mtc;
// Received/emitted MTC data breakdown:
RealTime m_mtcReceiveTime;
RealTime m_mtcEncodedTime;
int m_mtcFrames;
int m_mtcSeconds;
int m_mtcMinutes;
int m_mtcHours;
int m_mtcSMPTEType;
// Calculated MTC factors:
int m_mtcFirstTime;
RealTime m_mtcLastEncoded;
RealTime m_mtcLastReceive;
long long int m_mtcSigmaE;
long long int m_mtcSigmaC;
unsigned int m_mtcSkew;
bool m_looping;
bool m_haveShutdown;
#ifdef HAVE_LIBJACK
JackDriver *m_jackDriver;
#endif
Scavenger<RunnablePluginInstance> m_pluginScavenger;
//!!! -- hoist to SoundDriver w/setter?
typedef std::set<InstrumentId> InstrumentSet;
InstrumentSet m_recordingInstruments;
typedef std::map<DeviceId, ClientPortPair> DevicePortMap;
DevicePortMap m_devicePortMap;
typedef std::map<ClientPortPair, DeviceId> PortDeviceMap;
PortDeviceMap m_suspendedPortMap;
std::string getPortName(ClientPortPair port);
ClientPortPair getPortByName(std::string name);
DeviceId getSpareDeviceId();
struct AlsaTimerInfo {
int clas;
int sclas;
int card;
int device;
int subdevice;
std::string name;
long resolution;
};
std::vector<AlsaTimerInfo> m_timers;
std::string m_currentTimer;
// This auxiliary queue is here as a hack, to avoid stuck notes if
// resetting playback while a note-off is currently in the ALSA
// queue. When playback is reset by ffwd or rewind etc, we drop
// all the queued events (which is generally what is desired,
// except for note offs) and reset the queue timer (so the note
// offs would have the wrong time stamps even if we hadn't dropped
// them). Thus, we need to re-send any recent note offs before
// continuing. This queue records which note offs have been
// added to the ALSA queue recently.
//
NoteOffQueue m_recentNoteOffs;
void pushRecentNoteOffs(); // move from recent to normal queue after reset
void cropRecentNoteOffs(const RealTime &t); // remove old note offs
void weedRecentNoteOffs(unsigned int pitch, MidiByte channel,
InstrumentId instrument); // on subsequent note on
bool m_queueRunning;
bool m_portCheckNeeded;
enum { NeedNoJackStart, NeedJackReposition, NeedJackStart } m_needJackStart;
bool m_doTimerChecks;
bool m_firstTimerCheck;
double m_timerRatio;
bool m_timerRatioCalculated;
std::string getAlsaModuleVersionString();
std::string getKernelVersionString();
void extractVersion(std::string vstr, int &major, int &minor, int &subminor, std::string &suffix);
bool versionIsAtLeast(std::string vstr, int major, int minor, int subminor);
};
}
#endif // HAVE_ALSA
#endif // _ALSADRIVER_H_