summaryrefslogtreecommitdiffstats
path: root/src/sound/AlsaDriver.h
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/sound/AlsaDriver.h
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/sound/AlsaDriver.h')
-rw-r--r--src/sound/AlsaDriver.h561
1 files changed, 561 insertions, 0 deletions
diff --git a/src/sound/AlsaDriver.h b/src/sound/AlsaDriver.h
new file mode 100644
index 0000000..e80e30f
--- /dev/null
+++ b/src/sound/AlsaDriver.h
@@ -0,0 +1,561 @@
+// -*- c-indentation-style:"stroustrup" c-basic-offset: 4 -*-
+
+/*
+ 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<QString> *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,
+ QString 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 QStringList getPluginInstancePrograms(InstrumentId id,
+ int position) {
+#ifdef HAVE_LIBJACK
+ if (m_jackDriver) return m_jackDriver->getPluginInstancePrograms(id, position);
+#endif
+ return QStringList();
+ }
+
+ virtual QString getPluginInstanceProgram(InstrumentId id,
+ int position) {
+#ifdef HAVE_LIBJACK
+ if (m_jackDriver) return m_jackDriver->getPluginInstanceProgram(id, position);
+#endif
+ return QString();
+ }
+
+ virtual QString 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 QString();
+ }
+
+ virtual unsigned long getPluginInstanceProgram(InstrumentId id,
+ int position,
+ QString name) {
+#ifdef HAVE_LIBJACK
+ if (m_jackDriver) return m_jackDriver->getPluginInstanceProgram(id, position, name);
+#endif
+ return 0;
+ }
+
+ virtual void setPluginInstanceProgram(InstrumentId id,
+ int position,
+ QString program) {
+#ifdef HAVE_LIBJACK
+ if (m_jackDriver) m_jackDriver->setPluginInstanceProgram(id, position, program);
+#endif
+ }
+
+ virtual QString configurePlugin(InstrumentId id,
+ int position,
+ QString key,
+ QString value) {
+#ifdef HAVE_LIBJACK
+ if (m_jackDriver) return m_jackDriver->configurePlugin(id, position, key, value);
+#endif
+ return QString();
+ }
+
+ 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 insertMTCQFrames(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 handleMTCQFrame(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, QString name);
+
+ // Get available connections per device
+ //
+ virtual unsigned int getConnections(Device::DeviceType type,
+ MidiDevice::DeviceDirection direction);
+ virtual QString getConnection(Device::DeviceType type,
+ MidiDevice::DeviceDirection direction,
+ unsigned int connectionNo);
+ virtual void setConnection(DeviceId deviceId, QString connection);
+ virtual void setPlausibleConnection(DeviceId deviceId, QString connection);
+
+ virtual unsigned int getTimers();
+ virtual QString getTimer(unsigned int);
+ virtual QString getCurrentTimer();
+ virtual void setCurrentTimer(QString);
+
+ 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 QString 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, QString connection);
+ virtual void setConnectionToDevice(MappedDevice &device, QString 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_
+