summaryrefslogtreecommitdiffstats
path: root/src/sound/AudioProcess.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/AudioProcess.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/AudioProcess.h')
-rw-r--r--src/sound/AudioProcess.h390
1 files changed, 390 insertions, 0 deletions
diff --git a/src/sound/AudioProcess.h b/src/sound/AudioProcess.h
new file mode 100644
index 0000000..b517bc9
--- /dev/null
+++ b/src/sound/AudioProcess.h
@@ -0,0 +1,390 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ 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.
+*/
+
+#ifndef _AUDIO_PROCESS_H_
+#define _AUDIO_PROCESS_H_
+
+#include "SoundDriver.h"
+#include "Instrument.h"
+#include "RealTime.h"
+#include "RingBuffer.h"
+#include "RunnablePluginInstance.h"
+#include "AudioPlayQueue.h"
+#include "RecordableAudioFile.h"
+
+namespace Rosegarden
+{
+
+class AudioThread
+{
+public:
+ typedef float sample_t;
+
+ AudioThread(std::string name, // for diagnostics
+ SoundDriver *driver,
+ unsigned int sampleRate);
+
+ virtual ~AudioThread();
+
+ // This is to be called by the owning class after construction.
+ void run();
+
+ // This is to be called by the owning class to cause the thread to
+ // exit and clean up, before destruction.
+ void terminate();
+
+ bool running() const { return m_running; }
+
+ int getLock();
+ int tryLock();
+ int releaseLock();
+ void signal();
+
+protected:
+ virtual void threadRun() = 0;
+ virtual int getPriority() { return 0; }
+
+ std::string m_name;
+
+ SoundDriver *m_driver;
+ unsigned int m_sampleRate;
+
+ pthread_t m_thread;
+ pthread_mutex_t m_lock;
+ pthread_cond_t m_condition;
+ bool m_running;
+ volatile bool m_exiting;
+
+private:
+ static void *staticThreadRun(void *arg);
+ static void staticThreadCleanup(void *arg);
+};
+
+
+class AudioInstrumentMixer;
+
+class AudioBussMixer : public AudioThread
+{
+public:
+ AudioBussMixer(SoundDriver *driver,
+ AudioInstrumentMixer *instrumentMixer,
+ unsigned int sampleRate,
+ unsigned int blockSize);
+
+ virtual ~AudioBussMixer();
+
+ void kick(bool wantLock = true, bool signalInstrumentMixer = true);
+
+ /**
+ * Prebuffer. This should be called only when the transport is
+ * not running. This also calls fillBuffers on the instrument
+ * mixer.
+ */
+ void fillBuffers(const RealTime &currentTime);
+
+ /**
+ * Empty and discard buffer contents.
+ */
+ void emptyBuffers();
+
+ int getBussCount() {
+ return m_bussCount;
+ }
+
+ /**
+ * A buss is "dormant" if every readable sample on every one of
+ * its buffers is zero. It can therefore be safely skipped during
+ * playback.
+ */
+ bool isBussDormant(int buss) {
+ return m_bufferMap[buss].dormant;
+ }
+
+ /**
+ * Busses are currently always stereo.
+ */
+ RingBuffer<sample_t> *getRingBuffer(int buss, unsigned int channel) {
+ if (channel < m_bufferMap[buss].buffers.size()) {
+ return m_bufferMap[buss].buffers[channel];
+ } else {
+ return 0;
+ }
+ }
+
+ /// For call from MappedStudio. Pan is in range -100.0 -> 100.0
+ void setBussLevels(int buss, float dB, float pan);
+
+ /// For call regularly from anywhere in a non-RT thread
+ void updateInstrumentConnections();
+
+protected:
+ virtual void threadRun();
+
+ void processBlocks();
+ void generateBuffers();
+
+ AudioInstrumentMixer *m_instrumentMixer;
+ unsigned int m_blockSize;
+ int m_bussCount;
+
+ std::vector<sample_t *> m_processBuffers;
+
+ struct BufferRec
+ {
+ BufferRec() : dormant(true), buffers(), instruments(),
+ gainLeft(0.0), gainRight(0.0) { }
+ ~BufferRec();
+
+ bool dormant;
+
+ std::vector<RingBuffer<sample_t> *> buffers;
+ std::vector<bool> instruments; // index is instrument id minus base
+
+ float gainLeft;
+ float gainRight;
+ };
+
+ typedef std::map<int, BufferRec> BufferMap;
+ BufferMap m_bufferMap;
+};
+
+
+class AudioFileReader;
+class AudioFileWriter;
+
+class AudioInstrumentMixer : public AudioThread
+{
+public:
+ typedef std::vector<RunnablePluginInstance *> PluginList;
+ typedef std::map<InstrumentId, PluginList> PluginMap;
+ typedef std::map<InstrumentId, RunnablePluginInstance *> SynthPluginMap;
+
+ AudioInstrumentMixer(SoundDriver *driver,
+ AudioFileReader *fileReader,
+ unsigned int sampleRate,
+ unsigned int blockSize);
+
+ virtual ~AudioInstrumentMixer();
+
+ void kick(bool wantLock = true);
+
+ void setBussMixer(AudioBussMixer *mixer) { m_bussMixer = mixer; }
+
+ void setPlugin(InstrumentId id, int position, QString identifier);
+ void removePlugin(InstrumentId id, int position);
+ void removeAllPlugins();
+
+ void setPluginPortValue(InstrumentId id, int position,
+ unsigned int port, float value);
+ float getPluginPortValue(InstrumentId id, int position,
+ unsigned int port);
+
+ void setPluginBypass(InstrumentId, int position, bool bypass);
+
+ QStringList getPluginPrograms(InstrumentId, int);
+ QString getPluginProgram(InstrumentId, int);
+ QString getPluginProgram(InstrumentId, int, int, int);
+ unsigned long getPluginProgram(InstrumentId, int, QString);
+ void setPluginProgram(InstrumentId, int, QString);
+
+ QString configurePlugin(InstrumentId, int, QString, QString);
+
+ void resetAllPlugins(bool discardEvents = false);
+ void discardPluginEvents();
+ void destroyAllPlugins();
+
+ RunnablePluginInstance *getSynthPlugin(InstrumentId id) { return m_synths[id]; }
+
+ /**
+ * Return the plugins intended for a particular buss. (By coincidence,
+ * this will also work for instruments, but it's not to be relied on.)
+ * It's purely by historical accident that the instrument mixer happens
+ * to hold buss plugins as well -- this could do with being refactored.
+ */
+ PluginList &getBussPlugins(unsigned int bussId) { return m_plugins[bussId]; }
+
+ /**
+ * Return the total of the plugin latencies for a given instrument
+ * or buss id.
+ */
+ size_t getPluginLatency(unsigned int id);
+
+ /**
+ * Prebuffer. This should be called only when the transport is
+ * not running.
+ */
+ void fillBuffers(const RealTime &currentTime);
+
+ /**
+ * Ensure plugins etc have enough buffers. This is also done by
+ * fillBuffers and only needs to be called here if the extra work
+ * involved in fillBuffers is not desirable.
+ */
+ void allocateBuffers();
+
+ /**
+ * Empty and discard buffer contents.
+ */
+ void emptyBuffers(RealTime currentTime = RealTime::zeroTime);
+
+ /**
+ * An instrument is "empty" if it has no audio files, synths or
+ * plugins assigned to it, and so cannot generate sound. Empty
+ * instruments can safely be ignored during playback.
+ */
+ bool isInstrumentEmpty(InstrumentId id) {
+ return m_bufferMap[id].empty;
+ }
+
+ /**
+ * An instrument is "dormant" if every readable sample on every
+ * one of its buffers is zero. Dormant instruments can safely be
+ * skipped rather than mixed during playback, but they should not
+ * be ignored (unless also empty).
+ */
+ bool isInstrumentDormant(InstrumentId id) {
+ return m_bufferMap[id].dormant;
+ }
+
+ /**
+ * We always have at least two channels (and hence buffers) by
+ * this point, because even on a mono instrument we still have a
+ * Pan setting which will have been applied by the time we get to
+ * these buffers.
+ */
+ RingBuffer<sample_t, 2> *getRingBuffer(InstrumentId id, unsigned int channel) {
+ if (channel < m_bufferMap[id].buffers.size()) {
+ return m_bufferMap[id].buffers[channel];
+ } else {
+ return 0;
+ }
+ }
+
+ /// For call from MappedStudio. Pan is in range -100.0 -> 100.0
+ void setInstrumentLevels(InstrumentId instrument, float dB, float pan);
+
+ /// For call regularly from anywhere in a non-RT thread
+ void updateInstrumentMuteStates();
+
+protected:
+ virtual void threadRun();
+
+ virtual int getPriority() { return 3; }
+
+ void processBlocks(bool &readSomething);
+ void processEmptyBlocks(InstrumentId id);
+ bool processBlock(InstrumentId id, PlayableAudioFile **, size_t, bool &readSomething);
+ void generateBuffers();
+
+ AudioFileReader *m_fileReader;
+ AudioBussMixer *m_bussMixer;
+ unsigned int m_blockSize;
+
+ // The plugin data structures will all be pre-sized and so of
+ // fixed size during normal run time; this will allow us to add
+ // and edit plugins without locking.
+ RunnablePluginInstance *getPluginInstance(InstrumentId, int);
+ PluginMap m_plugins;
+ SynthPluginMap m_synths;
+
+ // maintain the same number of these as the maximum number of
+ // channels on any audio instrument
+ std::vector<sample_t *> m_processBuffers;
+
+ struct BufferRec
+ {
+ BufferRec() : empty(true), dormant(true), zeroFrames(0),
+ filledTo(RealTime::zeroTime), channels(2),
+ buffers(), gainLeft(0.0), gainRight(0.0), volume(0.0),
+ muted(false) { }
+ ~BufferRec();
+
+ bool empty;
+ bool dormant;
+ size_t zeroFrames;
+
+ RealTime filledTo;
+ size_t channels;
+ std::vector<RingBuffer<sample_t, 2> *> buffers;
+
+ float gainLeft;
+ float gainRight;
+ float volume;
+ bool muted;
+ };
+
+ typedef std::map<InstrumentId, BufferRec> BufferMap;
+ BufferMap m_bufferMap;
+};
+
+
+class AudioFileReader : public AudioThread
+{
+public:
+ AudioFileReader(SoundDriver *driver,
+ unsigned int sampleRate);
+
+ virtual ~AudioFileReader();
+
+ bool kick(bool wantLock = true);
+
+ /**
+ * Prebuffer. This should be called only when the transport is
+ * not running.
+ */
+ void fillBuffers(const RealTime &currentTime);
+
+protected:
+ virtual void threadRun();
+};
+
+
+class AudioFileWriter : public AudioThread
+{
+public:
+ AudioFileWriter(SoundDriver *driver,
+ unsigned int sampleRate);
+
+ virtual ~AudioFileWriter();
+
+ void kick(bool wantLock = true);
+
+ bool openRecordFile(InstrumentId id, const std::string &fileName);
+ bool closeRecordFile(InstrumentId id, AudioFileId &returnedId);
+
+ bool haveRecordFileOpen(InstrumentId id);
+ bool haveRecordFilesOpen();
+
+ void write(InstrumentId id, const sample_t *, int channel, size_t samples);
+
+protected:
+ virtual void threadRun();
+
+ typedef std::pair<AudioFile *, RecordableAudioFile *> FilePair;
+ typedef std::map<InstrumentId, FilePair> FileMap;
+ FileMap m_files;
+};
+
+
+}
+
+#endif
+