/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Rosegarden A MIDI and audio sequencer and musical notation editor. Copyright 2000-2011 the Rosegarden development team. 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 #include #include #ifdef HAVE_ALSA #include // ALSA #include "SoundDriver.h" #include "base/Instrument.h" #include "base/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 bool getMappedEventList(MappedEventList &composition); virtual bool record(RecordStatus recordStatus, const std::vector *armedInstruments = 0, const std::vector *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 MappedEventList &mC); virtual void processEventsOut(const MappedEventList &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 bool addDevice(Device::DeviceType type, DeviceId id, InstrumentId baseInstrumentId, MidiDevice::DeviceDirection direction); virtual void removeDevice(DeviceId id); virtual void removeAllDevices(); 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 QString getConnection(DeviceId id); virtual void setConnection(DeviceId deviceId, QString connection); virtual void setPlausibleConnection(DeviceId deviceId, QString connection, bool recordDevice = false); virtual void connectSomething(); 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; ssInstrumentCount = SoftSynthInstrumentCount; } 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 AlsaPortList; void clearDevices(); ClientPortPair getFirstDestination(bool duplex); ClientPortPair getPairForMappedInstrument(InstrumentId id); int getOutputPortForMappedInstrument(InstrumentId id); std::map > 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 generateFixedInstruments(); virtual void generateTimerList(); virtual std::string getAutoTimer(bool &wantTimerChecks); void addInstrumentsForDevice(MappedDevice *device, InstrumentId base); MappedDevice *createMidiDevice(DeviceId deviceId, MidiDevice::DeviceDirection); virtual void processMidiOut(const MappedEventList &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 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; // Track System Exclusive Event across several ALSA messages // ALSA mack break long system exclusive messages into chunks. typedef std::map > DeviceEventMap; DeviceEventMap *m_pendSysExcMap; /** * Clear all accumulated incompete System Exclusive messages. */ void clearPendSysExcMap(); #ifdef HAVE_LIBJACK JackDriver *m_jackDriver; #endif Scavenger m_pluginScavenger; //!!! -- hoist to SoundDriver w/setter? typedef std::set InstrumentSet; InstrumentSet m_recordingInstruments; typedef std::map DevicePortMap; DevicePortMap m_devicePortMap; std::string getPortName(ClientPortPair port); ClientPortPair getPortByName(std::string name); struct AlsaTimerInfo { int clas; int sclas; int card; int device; int subdevice; std::string name; long resolution; }; std::vector 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_