diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index 6e194cf77..a2a645c9f 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -34,17 +34,17 @@ #include -#include "AudioDevice.h" +#include "AudioBackend.h" class lcdSpinBox; class QComboBox; -class AudioAlsa : public AudioDevice, public QThread +class AudioAlsa : public AudioBackend, public QThread { public: - AudioAlsa( bool & _success_ful, mixer * _mixer ); + AudioAlsa( bool & _success_ful, AudioOutputContext * context ); virtual ~AudioAlsa(); inline static QString name() @@ -56,7 +56,7 @@ public: static QString probeDevice(); - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioBackend::setupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioDevice.h b/include/AudioBackend.h similarity index 60% rename from include/AudioDevice.h rename to include/AudioBackend.h index 660e2ca01..10d17c2da 100644 --- a/include/AudioDevice.h +++ b/include/AudioBackend.h @@ -1,5 +1,5 @@ /* - * AudioDevice.h - base-class for audio-devices, used by LMMS-mixer + * AudioBackend.h - base-class for audio-devices, used by LMMS-mixer * * Copyright (c) 2004-2009 Tobias Doerffel * @@ -26,37 +26,31 @@ #define _AUDIO_DEVICE_H #include -#include #include -#include "mixer.h" +#include "Mixer.h" #include "tab_widget.h" class AudioPort; - -class AudioDevice +/*! \brief The AudioBackend class is the base class for all kinds of AudioBackends. + * + * All classes derived from AudioBackend receive audio data so they can output + * it. + */ +class AudioBackend { public: - AudioDevice( const ch_cnt_t _channels, mixer * _mixer ); - virtual ~AudioDevice(); + /*! \brief Constructs an AudioBackend object for the given AudioOutputContext. */ + AudioBackend( const ch_cnt_t _channels, AudioOutputContext * context ); + virtual ~AudioBackend(); - inline void lock() - { - m_devMutex.lock(); - } - - inline void unlock() - { - m_devMutex.unlock(); - } - - - // if audio-driver supports ports, classes inherting AudioPort - // (e.g. channel-tracks) can register themselves for making - // audio-driver able to collect their individual output and provide - // them at a specific port - currently only supported by JACK + /*! If the audio backend supports ports, classes creating an AudioPort + * (e.g. InstrumentTrack) can register themselves for making + * audio backend able to collect their individual output and provide + * them at a specific port - currently only supported by JACK + */ virtual void registerPort( AudioPort * _port ); virtual void unregisterPort( AudioPort * _port ); virtual void renamePort( AudioPort * _port ); @@ -77,11 +71,14 @@ public: return m_channels; } - void processNextBuffer(); + /*! \brief Fetches one buffer and writes it to output device. + * + * \return Number of frames processed + */ + int processNextBuffer(); virtual void startProcessing() { - m_inProcess = true; } virtual void stopProcessing(); @@ -115,41 +112,47 @@ public: } ; + /*! \brief Returns const pointer to AudioOutputContext this AudioBackend acts for. */ + const AudioOutputContext * outputContext() const + { + return m_context; + } + + /*! \brief Returns const pointer to Mixer this AudioBackend acts for. */ + const Mixer * mixer() const; + protected: - // subclasses can re-implement this for being used in conjunction with - // processNextBuffer() + /*! \brief Writes given buffer to actual device. + * + * Subclasses can reimplement this for being used in conjunction with + * processNextBuffer() + */ virtual void writeBuffer( const sampleFrameA * /* _buf*/, const fpp_t /*_frames*/, const float /*_master_gain*/ ) { } - // called by according driver for fetching new sound-data - fpp_t getNextBuffer( sampleFrameA * _ab ); + /*! \brief Called by according backend for fetching new audio data. */ + int getNextBuffer( sampleFrameA * _ab ); - // clear given signed-int-16-buffer + /*! \brief Clears given signed-int-16-buffer. */ void clearS16Buffer( intSampleFrameA * _outbuf, const fpp_t _frames ); - // resample given buffer from samplerate _src_sr to samplerate _dst_sr - void resample( const sampleFrameA * _src, - const fpp_t _frames, - sampleFrameA * _dst, - const sample_rate_t _src_sr, - const sample_rate_t _dst_sr ); - inline void setSampleRate( const sample_rate_t _new_sr ) { m_sampleRate = _new_sr; } - mixer * getMixer() - { - return m_mixer; - } - bool hqAudio() const; + AudioOutputContext * outputContext() + { + return m_context; + } + + Mixer * mixer(); protected: @@ -157,15 +160,9 @@ protected: private: + AudioOutputContext * m_context; sample_rate_t m_sampleRate; ch_cnt_t m_channels; - mixer * m_mixer; - bool m_inProcess; - - QMutex m_devMutex; - - SRC_DATA m_srcData; - SRC_STATE * m_srcState; sampleFrameA * m_buffer; diff --git a/include/AudioDummy.h b/include/AudioDummy.h index 2a38bd622..324b08c85 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -25,16 +25,16 @@ #ifndef _AUDIO_DUMMY_H #define _AUDIO_DUMMY_H -#include "AudioDevice.h" +#include "AudioBackend.h" #include "Cpu.h" #include "MicroTimer.h" -class AudioDummy : public AudioDevice, public QThread +class AudioDummy : public AudioBackend, public QThread { public: - AudioDummy( bool & _success_ful, mixer * _mixer ) : - AudioDevice( DEFAULT_CHANNELS, _mixer ) + AudioDummy( bool & _success_ful, AudioOutputContext * context ) : + AudioBackend( DEFAULT_CHANNELS, context ) { _success_ful = true; } @@ -50,11 +50,11 @@ public: } - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioBackend::setupWidget { public: setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioDummy::name(), _parent ) + AudioBackend::setupWidget( AudioDummy::name(), _parent ) { } @@ -93,27 +93,25 @@ private: virtual void run() { MicroTimer timer; + sampleFrameA * buf = CPU::allocFrames( mixer()->framesPerPeriod() ); while( true ) { timer.reset(); - surroundSampleFrame * b = - getMixer()->nextBuffer(); - if( !b ) + int frames = getNextBuffer( buf ); + if( frames == 0 ) { break; } - CPU::freeFrames( b ); const Sint32 microseconds = static_cast( - getMixer()->framesPerPeriod() * - 1000000.0f / - getMixer()->processingSampleRate() - - timer.elapsed() ); + mixer()->framesPerPeriod() * 1000000.0f / + mixer()->processingSampleRate() - timer.elapsed() ); if( microseconds > 0 ) { usleep( microseconds ); } } + CPU::freeFrames( buf ); } } ; diff --git a/include/AudioFileDevice.h b/include/AudioFileDevice.h index cbc30b28f..b3ea33968 100644 --- a/include/AudioFileDevice.h +++ b/include/AudioFileDevice.h @@ -28,10 +28,10 @@ #include -#include "AudioDevice.h" +#include "AudioBackend.h" -class AudioFileDevice : public AudioDevice +class AudioFileDevice : public AudioBackend { public: AudioFileDevice( const sample_rate_t _sample_rate, @@ -41,7 +41,7 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ); + AudioOutputContext * context ); virtual ~AudioFileDevice(); QString outputFile() const @@ -108,7 +108,7 @@ typedef AudioFileDevice * ( * AudioFileDeviceInstantiaton ) const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ); + AudioOutputContext * mixer ); #endif diff --git a/include/AudioFileFlac.h b/include/AudioFileFlac.h index dd8ec6643..aaa3b2eeb 100644 --- a/include/AudioFileFlac.h +++ b/include/AudioFileFlac.h @@ -48,7 +48,7 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ); + AudioOutputContext * context ); virtual ~AudioFileFlac(); static AudioFileDevice * getInst( const sample_rate_t _sample_rate, @@ -60,13 +60,12 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ) + AudioOutputContext * context ) { return new AudioFileFlac( _sample_rate, _channels, _success_ful, _file, _use_vbr, _nom_bitrate, _min_bitrate, - _max_bitrate, _depth, - _mixer ); + _max_bitrate, _depth, context ); } diff --git a/include/AudioFileMp3.h b/include/AudioFileMp3.h index 7379fa4fa..6b5b6d8ea 100644 --- a/include/AudioFileMp3.h +++ b/include/AudioFileMp3.h @@ -44,7 +44,7 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ); + AudioOutputContext * context ); virtual ~AudioFileMp3(); static AudioFileDevice * getInst( const sample_rate_t _sample_rate, @@ -56,13 +56,12 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ) + AudioOutputContext * context ) { return new AudioFileMp3( _sample_rate, _channels, _success_ful, _file, _use_vbr, _nom_bitrate, _min_bitrate, - _max_bitrate, _depth, - _mixer ); + _max_bitrate, _depth, context ); } diff --git a/include/AudioFileOgg.h b/include/AudioFileOgg.h index 8ea9b9d51..363fbf8ab 100644 --- a/include/AudioFileOgg.h +++ b/include/AudioFileOgg.h @@ -47,7 +47,7 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ); + AudioOutputContext * context ); virtual ~AudioFileOgg(); static AudioFileDevice * getInst( const sample_rate_t _sample_rate, @@ -59,12 +59,11 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ) + AudioOutputContext * context ) { return new AudioFileOgg( _sample_rate, _channels, _success_ful, _file, _use_vbr, _nom_bitrate, - _min_bitrate, _max_bitrate, - _depth, _mixer ); + _min_bitrate, _max_bitrate, _depth, context ); } diff --git a/include/AudioFileWave.h b/include/AudioFileWave.h index 6446e35f6..eabe56d4a 100644 --- a/include/AudioFileWave.h +++ b/include/AudioFileWave.h @@ -1,5 +1,5 @@ /* - * AudioFileWave.h - AudioDevice which encodes wave-stream and writes it + * AudioFileWave.h - AudioBackend which encodes wave-stream and writes it * into a WAVE-file. This is used for song-export. * * Copyright (c) 2004-2009 Tobias Doerffel @@ -44,7 +44,7 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ); + AudioOutputContext * context ); virtual ~AudioFileWave(); static AudioFileDevice * getInst( const sample_rate_t _sample_rate, @@ -56,13 +56,12 @@ public: const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ) + AudioOutputContext * context ) { return new AudioFileWave( _sample_rate, _channels, _success_ful, _file, _use_vbr, _nom_bitrate, _min_bitrate, - _max_bitrate, _depth, - _mixer ); + _max_bitrate, _depth, context ); } diff --git a/include/AudioJack.h b/include/AudioJack.h index e9c018a95..86217d1ae 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -31,22 +31,22 @@ #include #endif -#include -#include #include +#include +#include -#include "AudioDevice.h" +#include "AudioBackend.h" class QLineEdit; class lcdSpinBox; -class AudioJack : public QObject, public AudioDevice +class AudioJack : public QObject, public AudioBackend { Q_OBJECT public: - AudioJack( bool & _success_ful, mixer * _mixer ); + AudioJack( bool & _success_ful, AudioOutputContext * context ); virtual ~AudioJack(); inline static QString name() @@ -56,7 +56,7 @@ public: } - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioBackend::setupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioOss.h b/include/AudioOss.h index 293694fe4..997b60657 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -29,17 +29,17 @@ #ifdef LMMS_HAVE_OSS -#include "AudioDevice.h" +#include "AudioBackend.h" class lcdSpinBox; class QLineEdit; -class AudioOss : public AudioDevice, public QThread +class AudioOss : public AudioBackend, public QThread { public: - AudioOss( bool & _success_ful, mixer * _mixer ); + AudioOss( bool & _success_ful, AudioOutputContext * context ); virtual ~AudioOss(); inline static QString name() @@ -50,7 +50,7 @@ public: static QString probeDevice(); - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioBackend::setupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioOutputContext.h b/include/AudioOutputContext.h new file mode 100644 index 000000000..8bd57ac38 --- /dev/null +++ b/include/AudioOutputContext.h @@ -0,0 +1,377 @@ +/* + * AudioOutputContext.h - centralize all audio output related functionality + * + * Copyright (c) 2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef _AUDIO_OUTPUT_CONTEXT_H +#define _AUDIO_OUTPUT_CONTEXT_H + +#include +#include + +#include "Mixer.h" + +class AudioBackend; + +/*! \brief The AudioOutputContext class centralizes all functionality + * and data related to output of audio data. + * + * The process of audio output is rather complicated due to different kinds of + * AudioBackend implementations, FIFO buffering and dedicated quality settings. + * The AudioOutputContext class handles all this so the Mixer class can just + * deal with actual audio rendering and processing. + */ +class AudioOutputContext +{ +public: + /*! \brief The QualitySettings class holds quality related settings. + * + * There's nothing special about it. It's just a data aggregration class. + */ + class QualitySettings + { + public: + /*! Lists all quality presets. */ + enum Preset + { + Preset_Draft, /*!< Draft quality - used for editing project */ + Preset_HighQuality, /*!< High quality - standard setting for project export */ + Preset_FinalMix, /*!< Final mix quality - very slow, best quality */ + NumPresets + } ; + + /*! Lists all supported interpolation types. */ + enum Interpolation + { + Interpolation_Linear, /*!< Linear interpolation - fast */ + Interpolation_SincFastest, /*!< Fastest Sinc interpolation - good quality */ + Interpolation_SincMedium, /*!< Medium Sinc interpolation - better quality */ + Interpolation_SincBest /*!< High quality interpolation */ + } ; + + /*! Lists all supported oversampling ratios. */ + enum Oversampling + { + Oversampling_None, /*!< No oversampling - fast */ + Oversampling_2x, /*!< 2x oversampling - good quality */ + Oversampling_4x, /*!< 4x oversampling - better quality */ + Oversampling_8x /*!< 8x oversampling - best quality but might break some filters */ + } ; + + /*! \brief Constructs a QualitySettings object based on a given preset. */ + QualitySettings( Preset m ) + { + switch( m ) + { + case Preset_Draft: + m_interpolation = Interpolation_Linear; + m_oversampling = Oversampling_None; + m_sampleExactControllers = false; + m_aliasFreeOscillators = false; + break; + case Preset_HighQuality: + m_interpolation = Interpolation_SincFastest; + m_oversampling = Oversampling_2x; + m_sampleExactControllers = true; + m_aliasFreeOscillators = false; + break; + case Preset_FinalMix: + m_interpolation = Interpolation_SincBest; + m_oversampling = Oversampling_8x; + m_sampleExactControllers = true; + m_aliasFreeOscillators = true; + break; + default: + break; + } + } + + /*! \brief Constructs a QualitySettings object based on specific quality settings. */ + QualitySettings( Interpolation _i, Oversampling _o, bool _sec, + bool _afo ) : + m_interpolation( _i ), + m_oversampling( _o ), + m_sampleExactControllers( _sec ), + m_aliasFreeOscillators( _afo ) + { + } + + /*! \brief Returns multiplier for sample rate based on oversampling settings. */ + int sampleRateMultiplier() const + { + switch( oversampling() ) + { + case Oversampling_None: return 1; + case Oversampling_2x: return 2; + case Oversampling_4x: return 4; + case Oversampling_8x: return 8; + } + return 1; + } + + /*! \brief Maps interpolation setting to libsamplerate constants. */ + int libsrcInterpolation() const + { + switch( interpolation() ) + { + case Interpolation_Linear: + return SRC_ZERO_ORDER_HOLD; + case Interpolation_SincFastest: + return SRC_SINC_FASTEST; + case Interpolation_SincMedium: + return SRC_SINC_MEDIUM_QUALITY; + case Interpolation_SincBest: + return SRC_SINC_BEST_QUALITY; + } + return SRC_LINEAR; + } + + /*! \brief Returns current interpolation setting. */ + Interpolation interpolation() const + { + return m_interpolation; + } + + /*! \brief Sets a new interpolation method. */ + void setInterpolation( Interpolation interpolation ) + { + m_interpolation = interpolation; + } + + /*! \brief Returns current oversampling setting. */ + Oversampling oversampling() const + { + return m_oversampling; + } + + /*! \brief Sets a new oversampling factor. */ + void setOversampling( Oversampling oversampling ) + { + m_oversampling = oversampling; + } + + /*! \brief Returns whether to use sample exact controllers. */ + bool sampleExactControllers() const + { + return m_sampleExactControllers; + } + + /*! \brief Returns whether to use alias free oscillators. */ + bool aliasFreeOscillators() const + { + return m_aliasFreeOscillators; + } + + private: + Interpolation m_interpolation; + Oversampling m_oversampling; + bool m_sampleExactControllers; + bool m_aliasFreeOscillators; + + } ; + + /*! \brief The BufferFifo class provides an internal FIFO for rendered buffers. + * + * When working with buffer sizes greater than the default buffer size, one + * big output buffer is still splitted into smaller chunks. This is + * especially neccessary for automation which takes place once a buffer + * period. Transitions would be anything else but smooth when adjusting a + * control just 20 times per second. BufferFifo handles the queueing of + * rendered buffers. */ + class BufferFifo + { + public: + /*! Each buffer in the FIFO can have a special state. This is used + * by FifoWriter to inject NULL buffers to indicate, the FIFO has + * been emptied after FifoWriter was told to finish. */ + enum BufferStates + { + Running, /*!< Regular buffer */ + NullBuffer /*!< Even if the buffer returned by currentReadBuffer() + * is not NULL, the FIFO input was NULL. FIFO reader can + * use this information for own purposes. */ + } ; + typedef BufferStates BufferState; + + /*! \brief Constructs a new BufferFifo object. + * + * \param size The number of buffers in the FIFO + * \param bufferSize The size of each buffer in the FIFO + */ + BufferFifo( int size, int bufferSize ); + ~BufferFifo(); + + /*! \brief Pushes a new buffer into the FIFO. + * + * You can also push NULL which will set the according buffer state + * to HasNullBuffer. */ + void write( sampleFrameA * buffer ); + + /*! \brief Prepares for reading next buffer (might block until one is available). */ + void startRead(); + + /*! \brief Returns current front buffer for reading. */ + sampleFrameA * currentReadBuffer() const + { + return m_buffers[m_readerIndex]; + } + + /*! \brief Returns state of current front buffer. */ + BufferState currentReadBufferState() const + { + return m_bufferStates[m_readerIndex]; + } + + /*! \brief Finish the current buffer read operation. + * + * The buffer returned by currentReadBuffer() is not guaranteed to + * be valid anymore after calling this function. */ + void finishRead(); + + /*! \brief Returns whether FIFO is empty. */ + bool isEmpty() const + { + return m_readerSem.available() == false; + } + + + private: + QSemaphore m_readerSem; + QSemaphore m_writerSem; + int m_readerIndex; + int m_writerIndex; + int m_size; + int m_bufferSize; + sampleFrameA * * m_buffers; + BufferState * m_bufferStates; + + } ; + + /*! \brief The FifoWriter class provides an internal thread for feeding + * the FIFO read by the active AudioBackend */ + class FifoWriter : public QThread + { + public: + FifoWriter( AudioOutputContext * context ); + + void finish(); + + + private: + AudioOutputContext * m_context; + volatile bool m_writing; + + virtual void run(); + + } ; + + /*! \brief Constructs an AudioOutputContext object for given AudioBackend. + * + * \param mixer The Mixer instance to fetch audio data from + * \param audioBackend The AudioBackend to write audio data to + * \param qualitySettings A QualitySettings object describing desired quality + */ + AudioOutputContext( Mixer * mixer, + AudioBackend * audioBackend, + const QualitySettings & qualitySettings ); + ~AudioOutputContext(); + + /*! \brief Sets an AudioBackend for this context. */ + void setAudioBackend( AudioBackend * backend ) + { + m_audioBackend = backend; + } + + /*! \brief Returns AudioBackend used by this context. */ + AudioBackend * audioBackend() + { + return m_audioBackend; + } + + /*! \brief Returns const AudioBackend used by this context. */ + const AudioBackend * audioBackend() const + { + return m_audioBackend; + } + + /*! \brief Returns Mixer used by this context. */ + Mixer * mixer() + { + return m_mixer; + } + + /*! \brief Returns const Mixer used by this context. */ + const Mixer * mixer() const + { + return m_mixer; + } + + /*! \brief Returns BufferFifo object used by this context. */ + BufferFifo * fifo() + { + return m_fifo; + } + + /*! \brief Returns current quality settings. */ + const QualitySettings & qualitySettings() const + { + return m_qualitySettings; + } + + /*! \brief Starts audio processing in this context. */ + void startProcessing(); + + /*! \brief Stops audio processing in this context. */ + void stopProcessing(); + + /*! \brief Returns whether audio processing in this context is running. */ + bool isProcessing() const; + + /*! \brief Copies current output buffer to destination buffer and optionally + * does resampling. + * + * If the Mixer has a running FifoWriter, it will make the FifoWriter start + * rendering the next buffer so it can be read from the Fifo next period + * without any delay. If the desired sample rate does not match current + * processing sample rate, resampling will be done. + * \param destBuffer The (aligned) destination buffer + * \param destSampleRate The desired output sample rate */ + int getCurrentOutputBuffer( sampleFrameA * destBuffer, + sample_rate_t destSampleRate ); + + +private: + Mixer * m_mixer; + QualitySettings m_qualitySettings; + AudioBackend * m_audioBackend; + BufferFifo * m_fifo; + FifoWriter * m_fifoWriter; + + // resample data + SRC_DATA m_srcData; + SRC_STATE * m_srcState; + + +} ; + + +#endif diff --git a/include/AudioPort.h b/include/AudioPort.h index 7157f729b..c005a7e6c 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -29,7 +29,7 @@ #include #include -#include "mixer.h" +#include "Mixer.h" class EffectChain; @@ -134,7 +134,7 @@ private: EffectChain * m_effects; - friend class mixer; + friend class Mixer; friend class MixerWorkerThread; } ; diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index a3f968729..3b2083168 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -47,7 +47,7 @@ public: #include -#include "AudioDevice.h" +#include "AudioBackend.h" #if defined paNeverDropInput || defined paNonInterleaved # define PORTAUDIO_V19 @@ -60,7 +60,7 @@ class comboBox; class lcdSpinBox; -class AudioPortAudio : public AudioDevice +class AudioPortAudio : public AudioBackend { public: AudioPortAudio( bool & _success_ful, mixer * _mixer ); @@ -77,7 +77,7 @@ public: unsigned long _framesPerBuffer ); - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioBackend::setupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index 33a11da1f..0a3a9513f 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -31,17 +31,17 @@ #include -#include "AudioDevice.h" +#include "AudioBackend.h" class lcdSpinBox; class QLineEdit; -class AudioPulseAudio : public AudioDevice, public QThread +class AudioPulseAudio : public AudioBackend, public QThread { public: - AudioPulseAudio( bool & _success_ful, mixer * _mixer ); + AudioPulseAudio( bool & _success_ful, AudioOutputContext * context ); virtual ~AudioPulseAudio(); inline static QString name() @@ -52,7 +52,7 @@ public: static QString probeDevice(); - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioBackend::setupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioSampleRecorder.h b/include/AudioSampleRecorder.h index e1e732f80..94a30e180 100644 --- a/include/AudioSampleRecorder.h +++ b/include/AudioSampleRecorder.h @@ -29,16 +29,16 @@ #include #include -#include "AudioDevice.h" +#include "AudioBackend.h" class sampleBuffer; -class AudioSampleRecorder : public AudioDevice +class AudioSampleRecorder : public AudioBackend { public: AudioSampleRecorder( const ch_cnt_t _channels, bool & _success_ful, - mixer * _mixer ); + AudioOutputContext * context ); virtual ~AudioSampleRecorder(); f_cnt_t framesRecorded() const; @@ -46,11 +46,11 @@ public: private: - virtual void writeBuffer( const surroundSampleFrame * _ab, + virtual void writeBuffer( const sampleFrameA * _ab, const fpp_t _frames, const float _master_gain ); - typedef QList > BufferList; + typedef QList > BufferList; BufferList m_buffers; } ; diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 19260829d..7826fcc69 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -29,18 +29,20 @@ #ifdef LMMS_HAVE_SDL +#include + #include #include -#include "AudioDevice.h" +#include "AudioBackend.h" class QLineEdit; -class AudioSdl : public AudioDevice +class AudioSdl : public AudioBackend { public: - AudioSdl( bool & _success_ful, mixer * _mixer ); + AudioSdl( bool & _success_ful, AudioOutputContext * context ); virtual ~AudioSdl(); inline static QString name() @@ -50,7 +52,7 @@ public: } - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioBackend::setupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/Controller.h b/include/Controller.h index 5ca110a53..129c44c18 100644 --- a/include/Controller.h +++ b/include/Controller.h @@ -23,13 +23,13 @@ * */ - #ifndef _CONTROLLER_H #define _CONTROLLER_H #include "engine.h" -#include "mixer.h" +#include "Mixer.h" #include "Model.h" +//#include "AudioOutputContext.h" #include "JournallingObject.h" class ControllerDialog; @@ -64,9 +64,9 @@ public: inline bool isSampleExact() const { - return m_sampleExact || - engine::getMixer()->currentQualitySettings(). - sampleExactControllers; + return m_sampleExact /*|| + engine::mixer()->audioOutputContext()-> + qualitySettings().sampleExactControllers()*/; } void setSampleExact( bool _exact ) @@ -76,7 +76,7 @@ public: inline ControllerTypes type() const { - return( m_type ); + return m_type; } // return whether this controller updates models frequently - used for @@ -85,17 +85,17 @@ public: { switch( m_type ) { - case LfoController: return( true ); - case PeakController: return( true ); + case LfoController: return true; + case PeakController: return true; default: break; } - return( false ); + return false; } virtual const QString & name() const { - return( m_name ); + return m_name; } diff --git a/include/Effect.h b/include/Effect.h index 4fff6b178..610d4f998 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -28,7 +28,7 @@ #include "Plugin.h" #include "engine.h" -#include "mixer.h" +#include "Mixer.h" #include "AutomatableModel.h" #include "TempoSyncKnobModel.h" diff --git a/include/EffectChain.h b/include/EffectChain.h index bf949f2bd..b09863896 100644 --- a/include/EffectChain.h +++ b/include/EffectChain.h @@ -28,7 +28,7 @@ #include "Model.h" #include "SerializingObject.h" -#include "mixer.h" +#include "Mixer.h" #include "AutomatableModel.h" class Effect; diff --git a/include/FxMixer.h b/include/FxMixer.h index 2a6bd84e0..9f7654bf5 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -26,7 +26,7 @@ #define _FX_MIXER_H #include "Model.h" -#include "mixer.h" +#include "Mixer.h" #include "EffectChain.h" #include "JournallingObject.h" diff --git a/include/Instrument.h b/include/Instrument.h index 09a60df38..8621dffa5 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -29,7 +29,7 @@ #include #include "Plugin.h" -#include "mixer.h" +#include "Mixer.h" // forward-declarations diff --git a/include/InstrumentSoundShaping.h b/include/InstrumentSoundShaping.h index 398df0688..eae4b9b69 100644 --- a/include/InstrumentSoundShaping.h +++ b/include/InstrumentSoundShaping.h @@ -25,7 +25,7 @@ #ifndef _INSTRUMENT_SOUND_SHAPING_H #define _INSTRUMENT_SOUND_SHAPING_H -#include "mixer.h" +#include "Mixer.h" #include "ComboBoxModel.h" diff --git a/include/mixer.h b/include/Mixer.h similarity index 58% rename from include/mixer.h rename to include/Mixer.h index 3562db3f1..16f5f4d3d 100644 --- a/include/mixer.h +++ b/include/Mixer.h @@ -1,5 +1,5 @@ /* - * mixer.h - audio-device-independent mixer for LMMS + * Mixer.h - Mixer for audio processing and rendering * * Copyright (c) 2004-2009 Tobias Doerffel * @@ -39,19 +39,18 @@ #include -#include #include #include #include "lmms_basics.h" #include "note.h" -#include "fifo_buffer.h" -class AudioDevice; -class MidiClient; +class AudioBackend; +class AudioOutputContext; class AudioPort; +class MidiClient; const fpp_t DEFAULT_BUFFER_SIZE = 256; @@ -67,105 +66,11 @@ const Octaves BaseOctave = DefaultOctave; class MixerWorkerThread; - -class EXPORT mixer : public QObject +/*! \brief The Mixer class is responsible for processing and rendering audio chunks. */ +class EXPORT Mixer : public QObject { Q_OBJECT public: - struct qualitySettings - { - enum Mode - { - Mode_Draft, - Mode_HighQuality, - Mode_FinalMix - } ; - - enum Interpolation - { - Interpolation_Linear, - Interpolation_SincFastest, - Interpolation_SincMedium, - Interpolation_SincBest - } ; - - enum Oversampling - { - Oversampling_None, - Oversampling_2x, - Oversampling_4x, - Oversampling_8x - } ; - - Interpolation interpolation; - Oversampling oversampling; - bool sampleExactControllers; - bool aliasFreeOscillators; - - qualitySettings( Mode _m ) - { - switch( _m ) - { - case Mode_Draft: - interpolation = Interpolation_Linear; - oversampling = Oversampling_None; - sampleExactControllers = false; - aliasFreeOscillators = false; - break; - case Mode_HighQuality: - interpolation = - Interpolation_SincFastest; - oversampling = Oversampling_2x; - sampleExactControllers = true; - aliasFreeOscillators = false; - break; - case Mode_FinalMix: - interpolation = Interpolation_SincBest; - oversampling = Oversampling_8x; - sampleExactControllers = true; - aliasFreeOscillators = true; - break; - } - } - - qualitySettings( Interpolation _i, Oversampling _o, bool _sec, - bool _afo ) : - interpolation( _i ), - oversampling( _o ), - sampleExactControllers( _sec ), - aliasFreeOscillators( _afo ) - { - } - - int sampleRateMultiplier() const - { - switch( oversampling ) - { - case Oversampling_None: return 1; - case Oversampling_2x: return 2; - case Oversampling_4x: return 4; - case Oversampling_8x: return 8; - } - return 1; - } - - int libsrcInterpolation() const - { - switch( interpolation ) - { - case Interpolation_Linear: - return SRC_ZERO_ORDER_HOLD; - case Interpolation_SincFastest: - return SRC_SINC_FASTEST; - case Interpolation_SincMedium: - return SRC_SINC_MEDIUM_QUALITY; - case Interpolation_SincBest: - return SRC_SINC_BEST_QUALITY; - } - return SRC_LINEAR; - } - } ; - void initDevices(); void clear(); @@ -176,16 +81,20 @@ public: return m_audioDevName; } - void setAudioDevice( AudioDevice * _dev ); - void setAudioDevice( AudioDevice * _dev, - const struct qualitySettings & _qs, - bool _needs_fifo ); - void restoreAudioDevice(); - inline AudioDevice * audioDev() + /*! \brief Sets a specific AudioOutputContext to be the active context. */ + void setAudioOutputContext( AudioOutputContext * context ); + const AudioOutputContext * audioOutputContext() const { - return m_audioDev; + return m_audioOutputContext; + } + AudioOutputContext * audioOutputContext() + { + return m_audioOutputContext; + } + AudioOutputContext * defaultAudioOutputContext() + { + return m_defaultAudioOutputContext; } - // audio-port-stuff inline void addAudioPort( AudioPort * _port ) @@ -246,7 +155,7 @@ public: return m_framesPerPeriod; } - inline const surroundSampleFrame * currentReadBuffer() const + inline const sampleFrameA * currentReadBuffer() const { return m_readBuf; } @@ -257,11 +166,6 @@ public: return m_cpuLoad; } - const qualitySettings & currentQualitySettings() const - { - return m_qualitySettings; - } - sample_rate_t baseSampleRate() const; sample_rate_t outputSampleRate() const; @@ -325,11 +229,6 @@ public: static void clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames, const f_cnt_t _offset = 0 ); -#ifndef LMMS_DISABLE_SURROUND - static void clearAudioBuffer( surroundSampleFrame * _ab, - const f_cnt_t _frames, - const f_cnt_t _offset = 0 ); -#endif static float peakValueLeft( sampleFrame * _ab, const f_cnt_t _frames ); static float peakValueRight( sampleFrame * _ab, const f_cnt_t _frames ); @@ -337,13 +236,8 @@ public: bool criticalXRuns() const; - inline bool hasFifoWriter() const - { - return m_fifoWriter != NULL; - } - void pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ); - + inline const sampleFrame * inputBuffer() { return m_inputBuffer[ m_inputBufferRead ]; @@ -354,56 +248,27 @@ public: return m_inputBufferFrames[ m_inputBufferRead ]; } - inline surroundSampleFrame * nextBuffer() - { - return hasFifoWriter() ? m_fifo->read() : renderNextBuffer(); - } - - void changeQuality( const struct qualitySettings & _qs ); + /*! \brief Processes and renders next chunk of audio. */ + sampleFrameA * renderNextBuffer(); signals: - void qualitySettingsChanged(); void sampleRateChanged(); void nextAudioBuffer(); private: - typedef fifoBuffer fifo; + Mixer(); + virtual ~Mixer(); - class fifoWriter : public QThread - { - public: - fifoWriter( mixer * _mixer, fifo * _fifo ); - - void finish(); - - - private: - mixer * m_mixer; - fifo * m_fifo; - volatile bool m_writing; - - virtual void run(); - - } ; - - - mixer(); - virtual ~mixer(); - - void startProcessing( bool _needs_fifo = true ); + void startProcessing(); void stopProcessing(); - AudioDevice * tryAudioDevices(); + AudioBackend * tryAudioBackends(); MidiClient * tryMidiClients(); - surroundSampleFrame * renderNextBuffer(); - - - QVector m_audioPorts; fpp_t m_framesPerPeriod; @@ -415,21 +280,21 @@ private: f_cnt_t m_inputBufferSize[2]; int m_inputBufferRead; int m_inputBufferWrite; - - surroundSampleFrame * m_readBuf; - surroundSampleFrame * m_writeBuf; - - QVector m_bufferPool; + + sampleFrameA * m_readBuf; + sampleFrameA * m_writeBuf; + + QVector m_bufferPool; int m_readBuffer; int m_writeBuffer; int m_poolDepth; - surroundSampleFrame m_maxClip; - surroundSampleFrame m_previousSample; + sampleFrame m_maxClip; + sampleFrame m_previousSample; fpp_t m_halfStart[SURROUND_CHANNELS]; bool m_oldBuffer[SURROUND_CHANNELS]; bool m_newBuffer[SURROUND_CHANNELS]; - + int m_cpuLoad; QVector m_workers; int m_numWorkers; @@ -439,12 +304,11 @@ private: PlayHandleList m_playHandles; ConstPlayHandleList m_playHandlesToRemove; - struct qualitySettings m_qualitySettings; float m_masterGain; - AudioDevice * m_audioDev; - AudioDevice * m_oldAudioDev; + AudioOutputContext * m_audioOutputContext; + AudioOutputContext * m_defaultAudioOutputContext; QString m_audioDevName; @@ -456,10 +320,6 @@ private: QMutex m_inputFramesMutex; - fifo * m_fifo; - fifoWriter * m_fifoWriter; - - friend class engine; friend class MixerWorkerThread; diff --git a/include/ProjectRenderer.h b/include/ProjectRenderer.h index f72b55c9c..68bcf1b7c 100644 --- a/include/ProjectRenderer.h +++ b/include/ProjectRenderer.h @@ -26,6 +26,7 @@ #define _PROJECT_RENDERER_H #include "AudioFileDevice.h" +#include "AudioOutputContext.h" #include "lmmsconfig.h" class QTimer; @@ -71,7 +72,7 @@ public: } ; - ProjectRenderer( const mixer::qualitySettings & _qs, + ProjectRenderer( const AudioOutputContext::QualitySettings & _qs, const OutputSettings & _os, ExportFileFormats _file_format, const QString & _out_file ); @@ -101,12 +102,15 @@ signals: void progressChanged( int ); +private slots: + void finishProcessing(); + + private: virtual void run(); AudioFileDevice * m_fileDev; - mixer::qualitySettings m_qualitySettings; - mixer::qualitySettings m_oldQualitySettings; + AudioOutputContext * m_context; volatile int m_progress; volatile bool m_abort; diff --git a/include/basic_filters.h b/include/basic_filters.h index ee986b0e2..2462978ee 100644 --- a/include/basic_filters.h +++ b/include/basic_filters.h @@ -36,7 +36,7 @@ #include #include "lmms_basics.h" -#include "mixer.h" +#include "Mixer.h" #include "templates.h" #include "lmms_constants.h" diff --git a/include/cpuload_widget.h b/include/cpuload_widget.h index 7a2d27696..4653f300d 100644 --- a/include/cpuload_widget.h +++ b/include/cpuload_widget.h @@ -51,7 +51,7 @@ protected slots: private: - Uint8 m_currentLoad; + int m_currentLoad; QPixmap m_temp; QPixmap m_background; diff --git a/include/engine.h b/include/engine.h index 0f9d471c3..4a17cee6b 100644 --- a/include/engine.h +++ b/include/engine.h @@ -41,7 +41,7 @@ class FxMixer; class FxMixerView; class ProjectJournal; class MainWindow; -class mixer; +class Mixer; class pianoRoll; class projectNotes; class ResourceDB; @@ -75,7 +75,12 @@ public: } // core - static mixer * getMixer() + static Mixer * getMixer() + { + return s_mixer; + } + + static Mixer * mixer() { return s_mixer; } @@ -207,7 +212,7 @@ private: static float s_framesPerTick; // core - static mixer * s_mixer; + static Mixer * s_mixer; static FxMixer * s_fxMixer; static song * s_song; static ResourceDB * s_workingDirResourceDB; diff --git a/include/fifo_buffer.h b/include/fifo_buffer.h index 4bf908611..e69de29bb 100644 --- a/include/fifo_buffer.h +++ b/include/fifo_buffer.h @@ -1,89 +0,0 @@ -/* - * fifo_buffer.h - FIFO fixed-size buffer - * - * Copyright (c) 2007 Javier Serrano Polo - * Copyright (c) 2008 Tobias Doerffel - * - * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#ifndef _FIFO_BUFFER_H -#define _FIFO_BUFFER_H - -#include - - -template -class fifoBuffer -{ -public: - fifoBuffer( int _size ) : - m_readerSem( _size ), - m_writerSem( _size ), - m_readerIndex( 0 ), - m_writerIndex( 0 ), - m_size( _size ) - { - m_buffer = new T[_size]; - m_readerSem.acquire( _size ); - } - - ~fifoBuffer() - { - delete[] m_buffer; - m_readerSem.release( m_size ); - } - - void write( T _element ) - { - m_writerSem.acquire(); - m_buffer[m_writerIndex++] = _element; - m_writerIndex %= m_size; - m_readerSem.release(); - } - - T read() - { - m_readerSem.acquire(); - T element = m_buffer[m_readerIndex++]; - m_readerIndex %= m_size; - m_writerSem.release(); - return element; - } - - bool available() - { - return m_readerSem.available(); - } - - -private: - QSemaphore m_readerSem; - QSemaphore m_writerSem; - int m_readerIndex; - int m_writerIndex; - int m_size; - T * m_buffer; - -} ; - - - - -#endif diff --git a/include/note_play_handle.h b/include/note_play_handle.h index 3341058dc..40b0c1d84 100644 --- a/include/note_play_handle.h +++ b/include/note_play_handle.h @@ -28,7 +28,7 @@ #define _NOTE_PLAY_HANDLE_H #include "lmmsconfig.h" -#include "mixer.h" +#include "Mixer.h" #include "note.h" #include "engine.h" #include "track.h" diff --git a/include/pch.h b/include/pch.h index 67aa73033..aa334c07d 100644 --- a/include/pch.h +++ b/include/pch.h @@ -31,6 +31,6 @@ #include -#include "mixer.h" +#include "Mixer.h" #endif diff --git a/include/sample_play_handle.h b/include/sample_play_handle.h index 2c1161737..2b590e892 100644 --- a/include/sample_play_handle.h +++ b/include/sample_play_handle.h @@ -26,7 +26,7 @@ #ifndef _SAMPLE_PLAY_HANDLE_H #define _SAMPLE_PLAY_HANDLE_H -#include "mixer.h" +#include "Mixer.h" #include "sample_buffer.h" #include "AutomatableModel.h" diff --git a/include/sample_record_handle.h b/include/sample_record_handle.h index f07cfd350..5afeb08f8 100644 --- a/include/sample_record_handle.h +++ b/include/sample_record_handle.h @@ -30,7 +30,7 @@ #include #include -#include "mixer.h" +#include "Mixer.h" #include "sample_buffer.h" class bbTrack; diff --git a/include/setup_dialog.h b/include/setup_dialog.h index 6f0dcb0ab..d0cd46b6b 100644 --- a/include/setup_dialog.h +++ b/include/setup_dialog.h @@ -29,7 +29,7 @@ #include #include "lmmsconfig.h" -#include "AudioDevice.h" +#include "AudioBackend.h" #include "MidiClient.h" #include "MidiPort.h" #include "MidiPortMenu.h" @@ -171,7 +171,7 @@ private: bool m_disableChActInd; bool m_manualChPiano; - typedef QMap AswMap; + typedef QMap AswMap; typedef QMap MswMap; typedef QMap trMap; diff --git a/include/surround_area.h b/include/surround_area.h index 6e1774728..986a1de49 100644 --- a/include/surround_area.h +++ b/include/surround_area.h @@ -31,7 +31,7 @@ #include #include "AutomatableModel.h" -#include "mixer.h" +#include "Mixer.h" class QPixmap; diff --git a/include/visualization_widget.h b/include/visualization_widget.h index 863a0ecca..8b8c3fd16 100644 --- a/include/visualization_widget.h +++ b/include/visualization_widget.h @@ -29,7 +29,7 @@ #include #include -#include "mixer.h" +#include "Mixer.h" class visualizationWidget : public QWidget diff --git a/plugins/ladspa_browser/ladspa_description.cpp b/plugins/ladspa_browser/ladspa_description.cpp index 209d0b89a..004a9996d 100644 --- a/plugins/ladspa_browser/ladspa_description.cpp +++ b/plugins/ladspa_browser/ladspa_description.cpp @@ -2,6 +2,7 @@ * ladspa_description.cpp - LADSPA plugin description * * Copyright (c) 2007 Javier Serrano Polo + * Copyright (c) 2009 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -30,10 +31,11 @@ #include #include -#include "AudioDevice.h" +#include "AudioBackend.h" +#include "AudioOutputContext.h" #include "engine.h" #include "ladspa_2_lmms.h" -#include "mixer.h" +#include "Mixer.h" @@ -73,8 +75,8 @@ ladspaDescription::ladspaDescription( QWidget * _parent, it != plugins.end(); it++ ) { if( _type != VALID || - manager->getDescription( ( *it ).second )->inputChannels - <= engine::getMixer()->audioDev()->channels() ) + manager->getDescription( ( *it ).second )->inputChannels <= + engine::mixer()->audioOutputContext()->audioBackend()->channels() ) { pluginNames.push_back( ( *it ).first ); m_pluginKeys.push_back( ( *it ).second ); diff --git a/plugins/ladspa_browser/ladspa_port_dialog.cpp b/plugins/ladspa_browser/ladspa_port_dialog.cpp index 3f324dbf4..0f2db8c28 100644 --- a/plugins/ladspa_browser/ladspa_port_dialog.cpp +++ b/plugins/ladspa_browser/ladspa_port_dialog.cpp @@ -2,7 +2,8 @@ * ladspa_port_dialog.cpp - dialog to test a LADSPA plugin * * Copyright (c) 2006-2008 Danny McRae - * + * Copyright (c) 2009 Tobias Doerffel + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or @@ -22,7 +23,6 @@ * */ - #include "ladspa_port_dialog.h" #include @@ -31,7 +31,7 @@ #include "embed.h" #include "engine.h" #include "ladspa_2_lmms.h" -#include "mixer.h" +#include "Mixer.h" ladspaPortDialog::ladspaPortDialog( const ladspa_key_t & _key ) @@ -95,11 +95,11 @@ ladspaPortDialog::ladspaPortDialog( const ladspa_key_t & _key ) { if( min != NOHINT ) { - min *= engine::getMixer()->processingSampleRate(); + min *= engine::mixer()->processingSampleRate(); } if( max != NOHINT ) { - max *= engine::getMixer()->processingSampleRate(); + max *= engine::mixer()->processingSampleRate(); } } diff --git a/plugins/ladspa_effect/LadspaEffect.cpp b/plugins/ladspa_effect/LadspaEffect.cpp index ea5d53e99..5dbff0ff6 100644 --- a/plugins/ladspa_effect/LadspaEffect.cpp +++ b/plugins/ladspa_effect/LadspaEffect.cpp @@ -25,14 +25,15 @@ #include +#include "AudioBackend.h" +#include "AudioOutputContext.h" #include "LadspaEffect.h" #include "mmp.h" -#include "AudioDevice.h" #include "config_mgr.h" #include "ladspa_2_lmms.h" #include "LadspaControl.h" #include "LadspaSubPluginFeatures.h" -#include "mixer.h" +#include "Mixer.h" #include "EffectChain.h" #include "Cpu.h" #include "automation_pattern.h" @@ -87,7 +88,7 @@ LadspaEffect::LadspaEffect( Model * _parent, pluginInstantiation(); - connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), + connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changeSampleRate() ) ); } @@ -144,13 +145,13 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, int frames = _frames; sampleFrame * o_buf = NULL; - if( m_maxSampleRate < engine::getMixer()->processingSampleRate() ) + if( m_maxSampleRate < engine::mixer()->processingSampleRate() ) { o_buf = _buf; _buf = CPU::allocFrames( _frames ); sampleDown( o_buf, _buf, m_maxSampleRate ); frames = _frames * m_maxSampleRate / - engine::getMixer()->processingSampleRate(); + engine::mixer()->processingSampleRate(); } // Copy the LMMS audio buffer to the LADSPA input buffer and initialize @@ -298,7 +299,8 @@ void LadspaEffect::pluginInstantiation() ladspa2LMMS * manager = engine::getLADSPAManager(); // Calculate how many processing units are needed. - const ch_cnt_t lmms_chnls = engine::getMixer()->audioDev()->channels(); + const ch_cnt_t lmms_chnls = engine::mixer()->audioOutputContext()-> + audioBackend()->channels(); int effect_channels = manager->getDescription( m_key )->inputChannels; setProcessorCount( lmms_chnls / effect_channels ); @@ -325,7 +327,7 @@ void LadspaEffect::pluginInstantiation() // during cleanup. It was easier to troubleshoot with the // memory management all taking place in one file. p->buffer = - new LADSPA_Data[engine::getMixer()->framesPerPeriod()]; + new LADSPA_Data[engine::mixer()->framesPerPeriod()]; if( p->name.toUpper().contains( "IN" ) && manager->isPortInput( m_key, port ) ) @@ -566,7 +568,7 @@ sample_rate_t LadspaEffect::maxSamplerate( const QString & _name ) { return __buggy_plugins[_name]; } - return engine::getMixer()->processingSampleRate(); + return engine::mixer()->processingSampleRate(); } diff --git a/plugins/ladspa_effect/LadspaSubPluginFeatures.cpp b/plugins/ladspa_effect/LadspaSubPluginFeatures.cpp index bb6cba447..35d49749e 100644 --- a/plugins/ladspa_effect/LadspaSubPluginFeatures.cpp +++ b/plugins/ladspa_effect/LadspaSubPluginFeatures.cpp @@ -28,12 +28,13 @@ #include #include +#include "AudioOutputContext.h" +#include "AudioBackend.h" #include "LadspaSubPluginFeatures.h" -#include "AudioDevice.h" #include "engine.h" #include "ladspa_2_lmms.h" #include "LadspaBase.h" -#include "mixer.h" +#include "Mixer.h" LadspaSubPluginFeatures::LadspaSubPluginFeatures( Plugin::PluginTypes _type ) : @@ -142,7 +143,7 @@ void LadspaSubPluginFeatures::listSubPluginKeys( it != plugins.end(); ++it ) { if( lm->getDescription( ( *it ).second )->inputChannels <= - engine::getMixer()->audioDev()->channels() ) + engine::mixer()->audioOutputContext()->audioBackend()->channels() ) { _kl.push_back( ladspaKeyToSubPluginKey( _desc, ( *it ).first, ( *it ).second ) ); } diff --git a/plugins/lb302/lb302.h b/plugins/lb302/lb302.h index 6094c4c53..7d4422769 100644 --- a/plugins/lb302/lb302.h +++ b/plugins/lb302/lb302.h @@ -37,7 +37,6 @@ #include "InstrumentView.h" #include "led_checkbox.h" #include "knob.h" -#include "mixer.h" class lb302SynthView; class notePlayHandle; diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index a93a125fd..56479236d 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -30,6 +30,8 @@ #include #include +#include "AudioBackend.h" +#include "AudioOutputContext.h" #include "ResourceFileMapper.h" #include "sf2_player.h" #include "engine.h" @@ -122,14 +124,14 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) : m_settings = new_fluid_settings(); fluid_settings_setint( m_settings, (char *) "audio.period-size", - engine::getMixer()->framesPerPeriod() ); + engine::mixer()->framesPerPeriod() ); // This is just our starting instance of synth. It is recreated // everytime we load a new soundfont. m_synth = new_fluid_synth( m_settings ); InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); - engine::getMixer()->addPlayHandle( iph ); + engine::mixer()->addPlayHandle( iph ); //loadFile( configManager::inst()->defaultSoundfont() ); @@ -147,7 +149,7 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) : connect( &m_patchNum, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) ); - connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), + connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) ); // Gain @@ -192,7 +194,7 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) : sf2Instrument::~sf2Instrument() { - engine::getMixer()->removePlayHandles( instrumentTrack() ); + engine::mixer()->removePlayHandles( instrumentTrack() ); freeFont(); delete_fluid_synth( m_synth ); delete_fluid_settings( m_settings ); @@ -498,7 +500,7 @@ void sf2Instrument::updateSampleRate() // Set & get, returns the true sample rate fluid_settings_setnum( m_settings, (char *) "synth.sample-rate", - engine::getMixer()->processingSampleRate() ); + engine::mixer()->processingSampleRate() ); fluid_settings_getnum( m_settings, (char *) "synth.sample-rate", &tempRate ); m_internalSampleRate = static_cast( tempRate ); @@ -529,8 +531,9 @@ void sf2Instrument::updateSampleRate() } m_synthMutex.lock(); - if( engine::getMixer()->currentQualitySettings().interpolation >= - mixer::qualitySettings::Interpolation_SincFastest ) + if( engine::mixer()->audioOutputContext()->qualitySettings(). + interpolation() >= + AudioOutputContext::QualitySettings::Interpolation_SincFastest ) { fluid_synth_set_interp_method( m_synth, -1, FLUID_INTERP_7THORDER ); @@ -541,7 +544,7 @@ void sf2Instrument::updateSampleRate() FLUID_INTERP_DEFAULT ); } m_synthMutex.unlock(); - if( m_internalSampleRate < engine::getMixer()->processingSampleRate() ) + if( m_internalSampleRate < engine::mixer()->processingSampleRate() ) { m_synthMutex.lock(); if( m_srcState != NULL ) @@ -549,9 +552,9 @@ void sf2Instrument::updateSampleRate() src_delete( m_srcState ); } int error; - m_srcState = src_new( engine::getMixer()-> - currentQualitySettings().libsrcInterpolation(), - DEFAULT_CHANNELS, &error ); + m_srcState = src_new( engine::mixer()->audioOutputContext()-> + qualitySettings().libsrcInterpolation(), + DEFAULT_CHANNELS, &error ); if( m_srcState == NULL || error ) { printf( "error while creating SRC-data-" diff --git a/plugins/vibed/vibrating_string.cpp b/plugins/vibed/vibrating_string.cpp index cdffd2caf..fc91a9036 100644 --- a/plugins/vibed/vibrating_string.cpp +++ b/plugins/vibed/vibrating_string.cpp @@ -26,7 +26,7 @@ #include "vibrating_string.h" #include "templates.h" #include "interpolation.h" -#include "mixer.h" +#include "Mixer.h" #include "engine.h" @@ -42,7 +42,7 @@ vibratingString::vibratingString( float _pitch, float _detune, bool _state ) : m_oversample( 2 * _oversample / (int)( _sample_rate / - engine::getMixer()->baseSampleRate() ) ), + engine::mixer()->baseSampleRate() ) ), m_randomize( _randomize ), m_stringLoss( 1.0f - _string_loss ), m_state( 0.1f ) diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index c365b9253..6d0fb7b10 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -29,7 +29,6 @@ #include #include -#include "mixer.h" #include "JournallingObject.h" #include "communication.h" diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index f65d9bee5..6bcde8f35 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -30,7 +30,7 @@ #include "song.h" #include "engine.h" -#include "mixer.h" +#include "Mixer.h" #include "Controller.h" #include "ControllerConnection.h" #include "ControllerDialog.h" diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index 0b346fd83..31c4ee4c5 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -30,7 +30,6 @@ #include "song.h" #include "engine.h" -#include "mixer.h" #include "ControllerConnection.h" diff --git a/src/core/Effect.cpp b/src/core/Effect.cpp index f848aacea..ebd595bbf 100644 --- a/src/core/Effect.cpp +++ b/src/core/Effect.cpp @@ -23,11 +23,11 @@ * */ - #include #include +#include "AudioOutputContext.h" #include "Effect.h" #include "engine.h" #include "DummyEffect.h" @@ -168,8 +168,8 @@ void Effect::reinitSRC() } int error; if( ( m_srcState[i] = src_new( - engine::getMixer()->currentQualitySettings(). - libsrcInterpolation(), + engine::mixer()->audioOutputContext()-> + qualitySettings().libsrcInterpolation(), DEFAULT_CHANNELS, &error ) ) == NULL ) { fprintf( stderr, "Error: src_new() failed in effect.cpp!\n" ); diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index dbf3c37d4..28209cb06 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -27,7 +27,7 @@ #include "EnvelopeAndLfoParameters.h" #include "debug.h" #include "engine.h" -#include "mixer.h" +#include "Mixer.h" #include "mmp.h" #include "Oscillator.h" diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 94d6cc687..696a11b73 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -31,7 +31,6 @@ #include "song.h" #include "engine.h" -#include "mixer.h" #include "LfoController.h" #include "ControllerDialog.h" diff --git a/src/core/mixer.cpp b/src/core/Mixer.cpp similarity index 76% rename from src/core/mixer.cpp rename to src/core/Mixer.cpp index b82d6886f..14313505b 100644 --- a/src/core/mixer.cpp +++ b/src/core/Mixer.cpp @@ -1,5 +1,5 @@ /* - * mixer.cpp - audio-device-independent mixer for LMMS + * Mixer.cpp - Mixer for audio processing and rendering * * Copyright (c) 2004-2009 Tobias Doerffel * @@ -24,7 +24,8 @@ #include -#include "mixer.h" +#include "AudioOutputContext.h" +#include "Mixer.h" #include "FxMixer.h" #include "play_handle.h" #include "song.h" @@ -117,7 +118,7 @@ public: static JobQueue s_jobQueue; - MixerWorkerThread( int _worker_num, mixer * _mixer ) : + MixerWorkerThread( int _worker_num, Mixer * _mixer ) : QThread( _mixer ), m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ), m_workerNum( _worker_num ), @@ -166,7 +167,7 @@ private: sampleFrame * m_workingBuf; int m_workerNum; volatile bool m_quit; - mixer * m_mixer; + Mixer * m_mixer; QWaitCondition * m_queueReadyWaitCond; } ; @@ -268,8 +269,11 @@ void MixerWorkerThread::processJobQueue() -mixer::mixer() : - m_framesPerPeriod( DEFAULT_BUFFER_SIZE ), + +Mixer::Mixer() : + m_framesPerPeriod( qBound( 32, + configManager::inst()->value( "mixer", "framesperaudiobuffer" ).toInt(), + DEFAULT_BUFFER_SIZE ) ), m_workingBuf( NULL ), m_inputBufferRead( 0 ), m_inputBufferWrite( 1 ), @@ -279,17 +283,16 @@ mixer::mixer() : m_workers(), m_numWorkers( QThread::idealThreadCount()-1 ), m_queueReadyWaitCond(), - m_qualitySettings( qualitySettings::Mode_Draft ), m_masterGain( 1.0f ), - m_audioDev( NULL ), - m_oldAudioDev( NULL ), + m_audioOutputContext( NULL ), + m_defaultAudioOutputContext( NULL ), m_globalMutex( QMutex::Recursive ) { for( int i = 0; i < 2; ++i ) { m_inputBufferFrames[i] = 0; m_inputBufferSize[i] = DEFAULT_BUFFER_SIZE * 100; - m_inputBuffer[i] = CPU::allocFrames( + m_inputBuffer[i] = CPU::allocFrames( DEFAULT_BUFFER_SIZE * 100 ); clearAudioBuffer( m_inputBuffer[i], m_inputBufferSize[i] ); } @@ -299,38 +302,6 @@ mixer::mixer() : __fx_channel_jobs[i-1] = (fx_ch_t) i; } - // just rendering? - if( !engine::hasGUI() ) - { - m_framesPerPeriod = DEFAULT_BUFFER_SIZE; - m_fifo = new fifo( 1 ); - } - else if( configManager::inst()->value( "mixer", "framesperaudiobuffer" - ).toInt() >= 32 ) - { - m_framesPerPeriod = - (fpp_t) configManager::inst()->value( "mixer", - "framesperaudiobuffer" ).toInt(); - - if( m_framesPerPeriod > DEFAULT_BUFFER_SIZE ) - { - m_fifo = new fifo( m_framesPerPeriod - / DEFAULT_BUFFER_SIZE ); - m_framesPerPeriod = DEFAULT_BUFFER_SIZE; - } - else - { - m_fifo = new fifo( 1 ); - } - } - else - { - configManager::inst()->setValue( "mixer", - "framesperaudiobuffer", - QString::number( m_framesPerPeriod ) ); - m_fifo = new fifo( 1 ); - } - m_workingBuf = CPU::allocFrames( m_framesPerPeriod ); for( Uint8 i = 0; i < 3; i++ ) { @@ -352,12 +323,17 @@ mixer::mixer() : m_poolDepth = 2; m_readBuffer = 0; m_writeBuffer = 1; + + // initialize default AudioOutputContext + m_defaultAudioOutputContext = new AudioOutputContext( this, NULL, + AudioOutputContext::QualitySettings::Preset_Draft ); + m_audioOutputContext = m_defaultAudioOutputContext; } -mixer::~mixer() +Mixer::~Mixer() { // distribute an empty job-queue so that worker-threads // get out of their processing-loop @@ -372,13 +348,7 @@ mixer::~mixer() m_workers[w]->wait( 500 ); } - while( m_fifo->available() ) - { - delete[] m_fifo->read(); - } - delete m_fifo; - - delete m_audioDev; + delete m_audioOutputContext; delete m_midiClient; for( Uint8 i = 0; i < 3; i++ ) @@ -392,54 +362,54 @@ mixer::~mixer() -void mixer::initDevices() +void Mixer::initDevices() { - m_audioDev = tryAudioDevices(); + audioOutputContext()->setAudioBackend( tryAudioBackends() ); m_midiClient = tryMidiClients(); } -void mixer::startProcessing( bool _needs_fifo ) +void Mixer::setAudioOutputContext( AudioOutputContext * context ) { - if( _needs_fifo ) - { - m_fifoWriter = new fifoWriter( this, m_fifo ); - m_fifoWriter->start( QThread::HighPriority ); - } - else - { - m_fifoWriter = NULL; - } + stopProcessing(); - m_audioDev->startProcessing(); + m_audioOutputContext = context; + + //m_audioDev->applyQualitySettings(); + + emit sampleRateChanged(); + + startProcessing(); } -void mixer::stopProcessing() +void Mixer::startProcessing() { - if( m_fifoWriter != NULL ) + if( m_audioOutputContext ) { - m_fifoWriter->finish(); - m_audioDev->stopProcessing(); - m_fifoWriter->wait( 1000 ); - m_fifoWriter->terminate(); - delete m_fifoWriter; - m_fifoWriter = NULL; - } - else - { - m_audioDev->stopProcessing(); + m_audioOutputContext->startProcessing(); } } -sample_rate_t mixer::baseSampleRate() const +void Mixer::stopProcessing() +{ + if( m_audioOutputContext ) + { + m_audioOutputContext->stopProcessing(); + } +} + + + + +sample_rate_t Mixer::baseSampleRate() const { sample_rate_t sr = configManager::inst()->value( "mixer", "samplerate" ).toInt(); @@ -453,33 +423,36 @@ sample_rate_t mixer::baseSampleRate() const -sample_rate_t mixer::outputSampleRate() const +sample_rate_t Mixer::outputSampleRate() const { - return m_audioDev != NULL ? m_audioDev->sampleRate() : - baseSampleRate(); + if( audioOutputContext()->audioBackend() ) + { + return audioOutputContext()->audioBackend()->sampleRate(); + } + return baseSampleRate(); } -sample_rate_t mixer::inputSampleRate() const +sample_rate_t Mixer::inputSampleRate() const { - return m_audioDev != NULL ? m_audioDev->sampleRate() : - baseSampleRate(); + return outputSampleRate(); } -sample_rate_t mixer::processingSampleRate() const +sample_rate_t Mixer::processingSampleRate() const { - return outputSampleRate() * m_qualitySettings.sampleRateMultiplier(); + return outputSampleRate() * + audioOutputContext()->qualitySettings().sampleRateMultiplier(); } -bool mixer::criticalXRuns() const +bool Mixer::criticalXRuns() const { return m_cpuLoad >= 99 && engine::getSong()->realTimeTask() == true; } @@ -487,14 +460,14 @@ bool mixer::criticalXRuns() const -void mixer::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ) +void Mixer::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ) { lockInputFrames(); f_cnt_t frames = m_inputBufferFrames[ m_inputBufferWrite ]; int size = m_inputBufferSize[ m_inputBufferWrite ]; sampleFrame * buf = m_inputBuffer[ m_inputBufferWrite ]; - + if( frames + _frames > size ) { size = qMax( size * 2, frames + _frames ); @@ -507,17 +480,17 @@ void mixer::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ) buf = ab; } - + CPU::memCpy( &buf[ frames ], _ab, _frames * sizeof( sampleFrame ) ); m_inputBufferFrames[ m_inputBufferWrite ] += _frames; - + unlockInputFrames(); } -sampleFrameA * mixer::renderNextBuffer() +sampleFrameA * Mixer::renderNextBuffer() { MicroTimer timer; static song::playPos last_metro_pos = -1; @@ -649,7 +622,7 @@ sampleFrameA * mixer::renderNextBuffer() // removes all play-handles. this is neccessary, when the song is stopped -> // all remaining notes etc. would be played until their end -void mixer::clear() +void Mixer::clear() { // TODO: m_midiClient->noteOffAll(); lock(); @@ -669,7 +642,7 @@ void mixer::clear() -void mixer::bufferToPort( const sampleFrame * _buf, +void Mixer::bufferToPort( const sampleFrame * _buf, const fpp_t _frames, const f_cnt_t _offset, stereoVolumeVector _vv, @@ -709,7 +682,7 @@ void mixer::bufferToPort( const sampleFrame * _buf, -void mixer::clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames, +void Mixer::clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames, const f_cnt_t _offset ) { if( likely( (size_t)( _ab+_offset ) % 16 == 0 && _frames % 8 == 0 ) ) @@ -724,18 +697,8 @@ void mixer::clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames, -#ifndef LMMS_DISABLE_SURROUND -void mixer::clearAudioBuffer( surroundSampleFrame * _ab, const f_cnt_t _frames, - const f_cnt_t _offset ) -{ - memset( _ab+_offset, 0, sizeof( *_ab ) * _frames ); -} -#endif - - - -float mixer::peakValueLeft( sampleFrame * _ab, const f_cnt_t _frames ) +float Mixer::peakValueLeft( sampleFrame * _ab, const f_cnt_t _frames ) { float p = 0.0f; for( f_cnt_t f = 0; f < _frames; ++f ) @@ -755,7 +718,7 @@ float mixer::peakValueLeft( sampleFrame * _ab, const f_cnt_t _frames ) -float mixer::peakValueRight( sampleFrame * _ab, const f_cnt_t _frames ) +float Mixer::peakValueRight( sampleFrame * _ab, const f_cnt_t _frames ) { float p = 0.0f; for( f_cnt_t f = 0; f < _frames; ++f ) @@ -775,97 +738,7 @@ float mixer::peakValueRight( sampleFrame * _ab, const f_cnt_t _frames ) -void mixer::changeQuality( const struct qualitySettings & _qs ) -{ - // don't delete the audio-device - stopProcessing(); - - m_qualitySettings = _qs; - m_audioDev->applyQualitySettings(); - - emit sampleRateChanged(); - emit qualitySettingsChanged(); - - startProcessing(); -} - - - - -void mixer::setAudioDevice( AudioDevice * _dev ) -{ - stopProcessing(); - - m_oldAudioDev = m_audioDev; - - if( _dev == NULL ) - { - printf( "param _dev == NULL in mixer::setAudioDevice(...). " - "Trying any working audio-device\n" ); - m_audioDev = tryAudioDevices(); - } - else - { - m_audioDev = _dev; - } - - emit sampleRateChanged(); - - startProcessing(); -} - - - - -void mixer::setAudioDevice( AudioDevice * _dev, - const struct qualitySettings & _qs, - bool _needs_fifo ) -{ - // don't delete the audio-device - stopProcessing(); - - m_qualitySettings = _qs; - m_oldAudioDev = m_audioDev; - - if( _dev == NULL ) - { - printf( "param _dev == NULL in mixer::setAudioDevice(...). " - "Trying any working audio-device\n" ); - m_audioDev = tryAudioDevices(); - } - else - { - m_audioDev = _dev; - } - - emit qualitySettingsChanged(); - emit sampleRateChanged(); - - startProcessing( _needs_fifo ); -} - - - - -void mixer::restoreAudioDevice() -{ - if( m_oldAudioDev != NULL ) - { - stopProcessing(); - delete m_audioDev; - - m_audioDev = m_oldAudioDev; - emit sampleRateChanged(); - - m_oldAudioDev = NULL; - startProcessing(); - } -} - - - - -void mixer::removeAudioPort( AudioPort * _port ) +void Mixer::removeAudioPort( AudioPort * _port ) { QVector::Iterator it = qFind( m_audioPorts.begin(), m_audioPorts.end(), @@ -881,7 +754,7 @@ void mixer::removeAudioPort( AudioPort * _port ) -void mixer::removePlayHandle( playHandle * _ph ) +void Mixer::removePlayHandle( playHandle * _ph ) { lock(); // check thread affinity as we must not delete play-handles @@ -908,7 +781,7 @@ void mixer::removePlayHandle( playHandle * _ph ) -void mixer::removePlayHandles( track * _track, playHandle::Type _type ) +void Mixer::removePlayHandles( track * _track, playHandle::Type _type ) { lock(); PlayHandleList::Iterator it = m_playHandles.begin(); @@ -932,10 +805,10 @@ void mixer::removePlayHandles( track * _track, playHandle::Type _type ) -AudioDevice * mixer::tryAudioDevices() +AudioBackend * Mixer::tryAudioBackends() { bool success_ful = false; - AudioDevice * dev = NULL; + AudioBackend * dev = NULL; QString dev_name = configManager::inst()->value( "mixer", "audiodev" ); if( dev_name == AudioDummy::name() ) @@ -946,7 +819,7 @@ AudioDevice * mixer::tryAudioDevices() #ifdef LMMS_HAVE_ALSA if( dev_name == AudioAlsa::name() || dev_name == "" ) { - dev = new AudioAlsa( success_ful, this ); + dev = new AudioAlsa( success_ful, audioOutputContext() ); if( success_ful ) { m_audioDevName = AudioAlsa::name(); @@ -960,7 +833,7 @@ AudioDevice * mixer::tryAudioDevices() #ifdef LMMS_HAVE_PORTAUDIO if( dev_name == AudioPortAudio::name() || dev_name == "" ) { - dev = new AudioPortAudio( success_ful, this ); + dev = new AudioPortAudio( success_ful, audioOutputContext() ); if( success_ful ) { m_audioDevName = AudioPortAudio::name(); @@ -974,7 +847,7 @@ AudioDevice * mixer::tryAudioDevices() #ifdef LMMS_HAVE_PULSEAUDIO if( dev_name == AudioPulseAudio::name() || dev_name == "" ) { - dev = new AudioPulseAudio( success_ful, this ); + dev = new AudioPulseAudio( success_ful, audioOutputContext() ); if( success_ful ) { m_audioDevName = AudioPulseAudio::name(); @@ -988,7 +861,7 @@ AudioDevice * mixer::tryAudioDevices() #ifdef LMMS_HAVE_OSS if( dev_name == AudioOss::name() || dev_name == "" ) { - dev = new AudioOss( success_ful, this ); + dev = new AudioOss( success_ful, audioOutputContext() ); if( success_ful ) { m_audioDevName = AudioOss::name(); @@ -1002,7 +875,7 @@ AudioDevice * mixer::tryAudioDevices() #ifdef LMMS_HAVE_JACK if( dev_name == AudioJack::name() || dev_name == "" ) { - dev = new AudioJack( success_ful, this ); + dev = new AudioJack( success_ful, audioOutputContext() ); if( success_ful ) { m_audioDevName = AudioJack::name(); @@ -1016,7 +889,7 @@ AudioDevice * mixer::tryAudioDevices() #ifdef LMMS_HAVE_SDL if( dev_name == AudioSdl::name() || dev_name == "" ) { - dev = new AudioSdl( success_ful, this ); + dev = new AudioSdl( success_ful, audioOutputContext() ); if( success_ful ) { m_audioDevName = AudioSdl::name(); @@ -1027,7 +900,7 @@ AudioDevice * mixer::tryAudioDevices() #endif // add more device-classes here... - //dev = new audioXXXX( SAMPLE_RATES[m_qualityLevel], success_ful, this ); + //dev = new audioXXXX( SAMPLE_RATES[m_qualityLevel], success_ful, audioOutputContext() ); //if( sucess_ful ) //{ // return dev; @@ -1040,13 +913,13 @@ AudioDevice * mixer::tryAudioDevices() m_audioDevName = AudioDummy::name(); - return new AudioDummy( success_ful, this ); + return new AudioDummy( success_ful, audioOutputContext() ); } -MidiClient * mixer::tryMidiClients() +MidiClient * Mixer::tryMidiClients() { QString client_name = configManager::inst()->value( "mixer", "mididev" ); @@ -1111,57 +984,5 @@ MidiClient * mixer::tryMidiClients() - - - - - - -mixer::fifoWriter::fifoWriter( mixer * _mixer, fifo * _fifo ) : - m_mixer( _mixer ), - m_fifo( _fifo ), - m_writing( true ) -{ -} - - - - -void mixer::fifoWriter::finish() -{ - m_writing = false; -} - - - - -void mixer::fifoWriter::run() -{ -#if 0 -#ifdef LMMS_BUILD_LINUX -#ifdef LMMS_HAVE_PTHREAD_H - cpu_set_t mask; - CPU_ZERO( &mask ); - CPU_SET( 0, &mask ); - pthread_setaffinity_np( pthread_self(), sizeof( mask ), &mask ); -#endif -#endif -#endif - - const fpp_t frames = m_mixer->framesPerPeriod(); - while( m_writing ) - { - sampleFrameA * buffer = CPU::allocFrames( frames ); - const sampleFrameA * b = m_mixer->renderNextBuffer(); - CPU::memCpy( buffer, b, frames * sizeof( sampleFrameA ) ); - m_fifo->write( buffer ); - } - - m_fifo->write( NULL ); -} - - - - -#include "moc_mixer.cxx" +#include "moc_Mixer.cxx" diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index ed00ef643..7b5c8a60c 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -24,7 +24,7 @@ #include "Oscillator.h" #include "engine.h" -#include "mixer.h" +#include "Mixer.h" #include "AutomatableModel.h" @@ -55,9 +55,9 @@ Oscillator::Oscillator( const IntModel * _wave_shape_model, void Oscillator::update( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { - if( m_freq >= engine::getMixer()->processingSampleRate() / 2 ) + if( m_freq >= engine::mixer()->processingSampleRate() / 2 ) { - mixer::clearAudioBuffer( _ab, _frames ); + Mixer::clearAudioBuffer( _ab, _frames ); return; } if( m_subOsc != NULL ) @@ -456,7 +456,7 @@ void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, recalcPhase(); const float osc_coeff = m_freq * m_detuning; const float sampleRateCorrection = 44100.0f / - engine::getMixer()->processingSampleRate(); + engine::mixer()->processingSampleRate(); for( fpp_t frame = 0; frame < _frames; ++frame ) { diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index 208304fb2..10ab65acb 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -32,7 +32,6 @@ #include "song.h" #include "engine.h" -#include "mixer.h" #include "PeakController.h" #include "ControllerDialog.h" #include "plugins/peak_controller_effect/peak_controller_effect.h" diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index 9836bdef2..c50521c37 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -29,7 +29,6 @@ #include "Plugin.h" #include "embed.h" #include "engine.h" -#include "mixer.h" #include "config_mgr.h" #include "DummyPlugin.h" #include "AutomatableModel.h" diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 63de13509..887f45f6e 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -77,17 +77,19 @@ FileEncodeDevice __fileEncodeDevices[] = const char * ProjectRenderer::EFF_ext[] = {"wav", "ogg", "mp3", "flac"}; -ProjectRenderer::ProjectRenderer( const mixer::qualitySettings & _qs, +ProjectRenderer::ProjectRenderer( + const AudioOutputContext::QualitySettings & _qs, const OutputSettings & _os, ExportFileFormats _file_format, const QString & _out_file ) : QThread( engine::getMixer() ), m_fileDev( NULL ), - m_qualitySettings( _qs ), - m_oldQualitySettings( engine::getMixer()->currentQualitySettings() ), m_progress( 0 ), m_abort( false ) { + m_context = new AudioOutputContext( engine::getMixer(), + NULL, + _qs ); if( __fileEncodeDevices[_file_format].m_getDevInst == NULL ) { return; @@ -100,13 +102,15 @@ ProjectRenderer::ProjectRenderer( const mixer::qualitySettings & _qs, _os.bitrate, _os.bitrate - 64, _os.bitrate + 64, _os.depth == Depth_32Bit ? 32 : ( _os.depth == Depth_24Bit ? 24 : 16 ), - engine::getMixer() ); + m_context ); if( success_ful == false ) { delete m_fileDev; m_fileDev = NULL; } + m_context->setAudioBackend( m_fileDev ); + } @@ -114,6 +118,7 @@ ProjectRenderer::ProjectRenderer( const mixer::qualitySettings & _qs, ProjectRenderer::~ProjectRenderer() { + delete m_fileDev; } @@ -129,12 +134,12 @@ ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension( { if( QString( __fileEncodeDevices[idx].m_extension ) == _ext ) { - return( __fileEncodeDevices[idx].m_fileFormat ); + return __fileEncodeDevices[idx].m_fileFormat; } ++idx; } - return( WaveFile ); // default + return WaveFile; // default } @@ -144,11 +149,12 @@ void ProjectRenderer::startProcessing() { if( isReady() ) { + connect( this, SIGNAL( finished() ), this, SLOT( finishProcessing() ) ); + // have to do mixer stuff with GUI-thread-affinity in order to // make slots connected to sampleRateChanged()-signals being // called immediately - engine::getMixer()->setAudioDevice( m_fileDev, - m_qualitySettings, false ); + engine::mixer()->setAudioOutputContext( m_context ); start( #ifndef LMMS_BUILD_WIN32 @@ -160,6 +166,49 @@ void ProjectRenderer::startProcessing() + +void ProjectRenderer::abortProcessing() +{ + m_abort = true; +} + + + + +void ProjectRenderer::updateConsoleProgress() +{ + const int cols = 50; + static int rot = 0; + char buf[80]; + char prog[cols+1]; + + if( m_fileDev == NULL ){ + qWarning("Error occured. Aborting render."); + m_consoleUpdateTimer->stop(); + delete m_consoleUpdateTimer; + // TODO: kill the program. I can't figure out how to do it... + return; + } + + for( int i = 0; i < cols; ++i ) + { + prog[i] = ( i*100/cols <= m_progress ? '-' : ' ' ); + } + prog[cols] = 0; + + const char * activity = (const char *) "|/-\\"; + memset( buf, 0, sizeof( buf ) ); + sprintf( buf, "\r|%s| %3d%% %c ", prog, m_progress, + activity[rot] ); + rot = ( rot+1 ) % 4; + + fprintf( stderr, "%s", buf ); + fflush( stderr ); +} + + + + void ProjectRenderer::run() { #if 0 @@ -194,11 +243,17 @@ void ProjectRenderer::run() } engine::getSong()->stopExport(); +} + + + +void ProjectRenderer::finishProcessing() +{ const QString f = m_fileDev->outputFile(); - engine::getMixer()->restoreAudioDevice(); // also deletes audio-dev - engine::getMixer()->changeQuality( m_oldQualitySettings ); + engine::mixer()->setAudioOutputContext( + engine::mixer()->defaultAudioOutputContext() ); // if the user aborted export-process, the file has to be deleted if( m_abort ) @@ -209,46 +264,5 @@ void ProjectRenderer::run() - -void ProjectRenderer::abortProcessing() -{ - m_abort = true; -} - - - -void ProjectRenderer::updateConsoleProgress() -{ - const int cols = 50; - static int rot = 0; - char buf[80]; - char prog[cols+1]; - - if( m_fileDev == NULL ){ - qWarning("Error occured. Aborting render."); - m_consoleUpdateTimer->stop(); - delete m_consoleUpdateTimer; - // TODO: kill the program. I can't figure out how to do it... - return; - } - - for( int i = 0; i < cols; ++i ) - { - prog[i] = ( i*100/cols <= m_progress ? '-' : ' ' ); - } - prog[cols] = 0; - - const char * activity = (const char *) "|/-\\"; - memset( buf, 0, sizeof( buf ) ); - sprintf( buf, "\r|%s| %3d%% %c ", prog, m_progress, - activity[rot] ); - rot = ( rot+1 ) % 4; - - fprintf( stderr, "%s", buf ); - fflush( stderr ); -} - - - #include "moc_ProjectRenderer.cxx" diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index e712cda80..a711d27cd 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -29,7 +29,7 @@ #endif #include "RemotePlugin.h" -#include "mixer.h" +#include "Mixer.h" #include "engine.h" #include "config_mgr.h" diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index b119e619a..077f5cfef 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -40,11 +40,11 @@ -AudioAlsa::AudioAlsa( bool & _success_ful, mixer * _mixer ) : - AudioDevice( tLimit( +AudioAlsa::AudioAlsa( bool & _success_ful, AudioOutputContext * context ) : + AudioBackend( tLimit( configManager::inst()->value( "audioalsa", "channels" ).toInt(), DEFAULT_CHANNELS, SURROUND_CHANNELS ), - _mixer ), + context ), m_handle( NULL ), m_hwParams( NULL ), m_swParams( NULL ), @@ -201,7 +201,7 @@ void AudioAlsa::applyQualitySettings() { if( hqAudio() ) { - setSampleRate( engine::getMixer()->processingSampleRate() ); + setSampleRate( mixer()->processingSampleRate() ); if( m_handle != NULL ) { @@ -233,8 +233,6 @@ void AudioAlsa::applyQualitySettings() return; } } - - AudioDevice::applyQualitySettings(); } @@ -242,16 +240,15 @@ void AudioAlsa::applyQualitySettings() void AudioAlsa::run() { - sampleFrameA * temp = CPU::allocFrames( - getMixer()->framesPerPeriod() ); + sampleFrameA * temp = CPU::allocFrames( mixer()->framesPerPeriod() ); intSampleFrameA * outbuf = (intSampleFrameA *) CPU::memAlloc( sizeof( intSampleFrameA ) * channels() / - DEFAULT_CHANNELS * getMixer()->framesPerPeriod() ); + DEFAULT_CHANNELS * mixer()->framesPerPeriod() ); int_sample_t * pcmbuf = new int_sample_t[m_periodSize * channels()]; - int outbuf_size = getMixer()->framesPerPeriod() * channels(); + int outbuf_size = mixer()->framesPerPeriod() * channels(); int outbuf_pos = 0; int pcmbuf_size = m_periodSize * channels(); @@ -274,7 +271,7 @@ void AudioAlsa::run() outbuf_size = frames * channels(); CPU::convertToS16( temp, outbuf, frames, - getMixer()->masterGain(), + mixer()->masterGain(), m_convertEndian ); } int min_len = qMin( len, outbuf_size - outbuf_pos ); @@ -374,7 +371,7 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) sampleRate(), 0 ) ) < 0 ) { if( ( err = snd_pcm_hw_params_set_rate( m_handle, m_hwParams, - getMixer()->baseSampleRate(), 0 ) ) < 0 ) + mixer()->baseSampleRate(), 0 ) ) < 0 ) { printf( "Could not set sample rate: %s\n", snd_strerror( err ) ); @@ -382,7 +379,7 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) } } - m_periodSize = getMixer()->framesPerPeriod(); + m_periodSize = mixer()->framesPerPeriod(); m_bufferSize = m_periodSize * 8; dir = 0; err = snd_pcm_hw_params_set_period_size_near( m_handle, m_hwParams, @@ -493,7 +490,7 @@ int AudioAlsa::setSWParams() AudioAlsa::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioAlsa::name(), _parent ) + AudioBackend::setupWidget( AudioAlsa::name(), _parent ) { m_device = new QComboBox( this ); diff --git a/src/core/audio/AudioBackend.cpp b/src/core/audio/AudioBackend.cpp new file mode 100644 index 000000000..0b3607049 --- /dev/null +++ b/src/core/audio/AudioBackend.cpp @@ -0,0 +1,144 @@ +/* + * AudioBackend.cpp - base-class for audio-devices used by LMMS-mixer + * + * Copyright (c) 2004-2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "AudioBackend.h" +#include "AudioOutputContext.h" +#include "config_mgr.h" +#include "debug.h" +#include "Cpu.h" + + + +AudioBackend::AudioBackend( const ch_cnt_t _channels, + AudioOutputContext * context ) : + m_supportsCapture( false ), + m_context( context ), + m_sampleRate( mixer()->processingSampleRate() ), + m_channels( _channels ), + m_buffer( CPU::allocFrames( mixer()->framesPerPeriod() ) ) +{ +} + + + + +AudioBackend::~AudioBackend() +{ + CPU::freeFrames( m_buffer ); +} + + + + +int AudioBackend::processNextBuffer() +{ + const int frames = getNextBuffer( m_buffer ); + if( frames ) + { + writeBuffer( m_buffer, frames, mixer()->masterGain() ); + } + return frames; +} + + + + +int AudioBackend::getNextBuffer( sampleFrameA * _ab ) +{ + return outputContext()->getCurrentOutputBuffer( _ab, sampleRate() ); +} + + + + +void AudioBackend::stopProcessing() +{ + // flush AudioOutputContext's FIFO + while( processNextBuffer() ) + { + } +} + + + + +void AudioBackend::applyQualitySettings() +{ +} + + + + +void AudioBackend::registerPort( AudioPort * ) +{ +} + + + + +void AudioBackend::unregisterPort( AudioPort * _port ) +{ +} + + + + +void AudioBackend::renamePort( AudioPort * ) +{ +} + + + + +void AudioBackend::clearS16Buffer( intSampleFrameA * _outbuf, const fpp_t _frames ) +{ + CPU::memClear( _outbuf, _frames * sizeof( *_outbuf ) ); +// memset( _outbuf, 0, _frames * channels() * BYTES_PER_INT_SAMPLE ); +} + + + + +bool AudioBackend::hqAudio() const +{ + return configManager::inst()->value( "mixer", "hqaudio" ).toInt(); +} + + + + +const Mixer * AudioBackend::mixer() const +{ + return outputContext()->mixer(); +} + + + + +Mixer * AudioBackend::mixer() +{ + return outputContext()->mixer(); +} + + diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp deleted file mode 100644 index ba38a227f..000000000 --- a/src/core/audio/AudioDevice.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * AudioDevice.cpp - base-class for audio-devices used by LMMS-mixer - * - * Copyright (c) 2004-2009 Tobias Doerffel - * - * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#include "AudioDevice.h" -#include "config_mgr.h" -#include "debug.h" -#include "Cpu.h" - - - -AudioDevice::AudioDevice( const ch_cnt_t _channels, mixer * _mixer ) : - m_supportsCapture( false ), - m_sampleRate( _mixer->processingSampleRate() ), - m_channels( _channels ), - m_mixer( _mixer ), - m_buffer( CPU::allocFrames( getMixer()->framesPerPeriod() ) ) -{ - int error; - if( ( m_srcState = src_new( - getMixer()->currentQualitySettings().libsrcInterpolation(), - SURROUND_CHANNELS, &error ) ) == NULL ) - { - printf( "Error: src_new() failed in audio_device.cpp!\n" ); - } -} - - - - -AudioDevice::~AudioDevice() -{ - src_delete( m_srcState ); - CPU::freeFrames( m_buffer ); - - m_devMutex.tryLock(); - unlock(); -} - - - - -void AudioDevice::processNextBuffer() -{ - const fpp_t frames = getNextBuffer( m_buffer ); - if( frames ) - { - writeBuffer( m_buffer, frames, getMixer()->masterGain() ); - } - else - { - m_inProcess = false; - } -} - - - - -fpp_t AudioDevice::getNextBuffer( sampleFrameA * _ab ) -{ - fpp_t frames = getMixer()->framesPerPeriod(); - sampleFrameA * b = getMixer()->nextBuffer(); - if( !b ) - { - return 0; - } - - // make sure, no other thread is accessing device - lock(); - - // resample if neccessary - if( getMixer()->processingSampleRate() != m_sampleRate ) - { - resample( b, frames, _ab, getMixer()->processingSampleRate(), - m_sampleRate ); - frames = frames * m_sampleRate / - getMixer()->processingSampleRate(); - } - else - { - CPU::memCpy( _ab, b, frames * sizeof( surroundSampleFrame ) ); - } - - // release lock - unlock(); - - if( getMixer()->hasFifoWriter() ) - { - CPU::freeFrames( b ); - } - - return frames; -} - - - - -void AudioDevice::stopProcessing() -{ - if( getMixer()->hasFifoWriter() ) - { - while( m_inProcess ) - { - processNextBuffer(); - } - } -} - - - - -void AudioDevice::applyQualitySettings() -{ - src_delete( m_srcState ); - - int error; - if( ( m_srcState = src_new( - getMixer()->currentQualitySettings().libsrcInterpolation(), - SURROUND_CHANNELS, &error ) ) == NULL ) - { - printf( "Error: src_new() failed in audio_device.cpp!\n" ); - } -} - - - - -void AudioDevice::registerPort( AudioPort * ) -{ -} - - - - -void AudioDevice::unregisterPort( AudioPort * _port ) -{ -} - - - - -void AudioDevice::renamePort( AudioPort * ) -{ -} - - - - -void AudioDevice::resample( const sampleFrame * _src, const fpp_t _frames, - sampleFrame * _dst, - const sample_rate_t _src_sr, - const sample_rate_t _dst_sr ) -{ - if( m_srcState == NULL ) - { - return; - } - m_srcData.input_frames = _frames; - m_srcData.output_frames = _frames; - m_srcData.data_in = (float *) _src[0]; - m_srcData.data_out = _dst[0]; - m_srcData.src_ratio = (double) _dst_sr / _src_sr; - m_srcData.end_of_input = 0; - int error; - if( ( error = src_process( m_srcState, &m_srcData ) ) ) - { - printf( "AudioDevice::resample(): error while resampling: %s\n", - src_strerror( error ) ); - } -} - - - -void AudioDevice::clearS16Buffer( intSampleFrameA * _outbuf, const fpp_t _frames ) -{ - CPU::memClear( _outbuf, _frames * sizeof( *_outbuf ) ); -// memset( _outbuf, 0, _frames * channels() * BYTES_PER_INT_SAMPLE ); -} - - - - -bool AudioDevice::hqAudio() const -{ - return configManager::inst()->value( "mixer", "hqaudio" ).toInt(); -} - - diff --git a/src/core/audio/AudioFileDevice.cpp b/src/core/audio/AudioFileDevice.cpp index e57c2a883..e92f41b47 100644 --- a/src/core/audio/AudioFileDevice.cpp +++ b/src/core/audio/AudioFileDevice.cpp @@ -39,8 +39,8 @@ AudioFileDevice::AudioFileDevice( const sample_rate_t _sample_rate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ) : - AudioDevice( _channels, _mixer ), + AudioOutputContext * context ) : + AudioBackend( _channels, context ), m_outputFile( _file ), m_useVbr( _use_vbr ), m_nomBitrate( _nom_bitrate ), diff --git a/src/core/audio/AudioFileFlac.cpp b/src/core/audio/AudioFileFlac.cpp index cfcc7fc51..77e2fa731 100644 --- a/src/core/audio/AudioFileFlac.cpp +++ b/src/core/audio/AudioFileFlac.cpp @@ -37,9 +37,9 @@ AudioFileFlac::AudioFileFlac( const sample_rate_t _sample_rate, const ch_cnt_t _channels, bool & _success_ful, const QString & _file, const bool _use_vbr, const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, - const int _depth, mixer * _mixer ) : + const int _depth, AudioOutputContext * context ) : AudioFileDevice( _sample_rate, _channels, _file, _use_vbr, _nom_bitrate, - _min_bitrate, _max_bitrate, _depth, _mixer ) + _min_bitrate, _max_bitrate, _depth, context ) { _success_ful = startEncoding(); } diff --git a/src/core/audio/AudioFileMp3.cpp b/src/core/audio/AudioFileMp3.cpp index e9569c99b..72252066e 100644 --- a/src/core/audio/AudioFileMp3.cpp +++ b/src/core/audio/AudioFileMp3.cpp @@ -38,9 +38,9 @@ AudioFileMp3::AudioFileMp3( const sample_rate_t _sample_rate, const ch_cnt_t _channels, bool & _success_ful, const QString & _file, const bool _use_vbr, const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, - const int _depth, mixer * _mixer ) : + const int _depth, AudioOutputContext * context ) : AudioFileDevice( _sample_rate, _channels, _file, _use_vbr, _nom_bitrate, - _min_bitrate, _max_bitrate, _depth, _mixer ), + _min_bitrate, _max_bitrate, _depth, context ), m_lgf( NULL ), m_lame( LameLibrary() ), m_outfile( NULL ), diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index 3ef2856e7..781a3468a 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -44,10 +44,10 @@ AudioFileOgg::AudioFileOgg( const sample_rate_t _sample_rate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ) : + AudioOutputContext * context ) : AudioFileDevice( _sample_rate, _channels, _file, _use_vbr, _nom_bitrate, _min_bitrate, _max_bitrate, - _depth, _mixer ) + _depth, context ) { m_ok = _success_ful = startEncoding(); } diff --git a/src/core/audio/AudioFileWave.cpp b/src/core/audio/AudioFileWave.cpp index b15ab339e..f52b784dd 100644 --- a/src/core/audio/AudioFileWave.cpp +++ b/src/core/audio/AudioFileWave.cpp @@ -36,10 +36,10 @@ AudioFileWave::AudioFileWave( const sample_rate_t _sample_rate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, const int _depth, - mixer * _mixer ) : + AudioOutputContext * context ) : AudioFileDevice( _sample_rate, _channels, _file, _use_vbr, _nom_bitrate, _min_bitrate, _max_bitrate, - _depth, _mixer ) + _depth, context ) { _success_ful = startEncoding(); } @@ -59,7 +59,7 @@ bool AudioFileWave::startEncoding() { m_si.samplerate = sampleRate(); m_si.channels = channels(); - m_si.frames = getMixer()->framesPerPeriod(); + m_si.frames = mixer()->framesPerPeriod(); m_si.sections = 1; m_si.seekable = 0; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 737f5d462..9eb0856f5 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -45,15 +45,15 @@ -AudioJack::AudioJack( bool & _success_ful, mixer * _mixer ) : - AudioDevice( tLimit( configManager::inst()->value( +AudioJack::AudioJack( bool & _success_ful, AudioOutputContext * context ) : + AudioBackend( tLimit( configManager::inst()->value( "audiojack", "channels" ).toInt(), DEFAULT_CHANNELS, SURROUND_CHANNELS ), - _mixer ), + context ), m_client( NULL ), m_active( false ), m_stopSemaphore( 1 ), - m_outBuf( CPU::allocFrames( getMixer()->framesPerPeriod() ) ), + m_outBuf( CPU::allocFrames( mixer()->framesPerPeriod() ) ), m_framesDoneInCurBuf( 0 ), m_framesToDoInCurBuf( 0 ) { @@ -210,7 +210,7 @@ void AudioJack::startProcessing() // try to sync JACK's and LMMS's buffer-size -// jack_set_buffer_size( m_client, getMixer()->framesPerPeriod() ); +// jack_set_buffer_size( m_client, mixer()->framesPerPeriod() ); @@ -255,15 +255,13 @@ void AudioJack::applyQualitySettings() { if( hqAudio() ) { - setSampleRate( engine::getMixer()->processingSampleRate() ); + setSampleRate( mixer()->processingSampleRate() ); if( jack_get_sample_rate( m_client ) != sampleRate() ) { setSampleRate( jack_get_sample_rate( m_client ) ); } } - - AudioDevice::applyQualitySettings(); } @@ -343,7 +341,7 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata ) #ifdef AUDIO_PORT_SUPPORT const Uint32 frames = qMin( _nframes, - getMixer()->framesPerPeriod() ); + mixer()->framesPerPeriod() ); for( jackPortMap::iterator it = m_portMap.begin(); it != m_portMap.end(); ++it ) { @@ -372,7 +370,7 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata ) _nframes, m_framesToDoInCurBuf - m_framesDoneInCurBuf ); - const float gain = getMixer()->masterGain(); + const float gain = mixer()->masterGain(); for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { jack_default_audio_sample_t * o = outbufs[chnl]; @@ -434,7 +432,7 @@ void AudioJack::shutdownCallback( void * _udata ) AudioJack::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioJack::name(), _parent ) + AudioBackend::setupWidget( AudioJack::name(), _parent ) { QString cn = configManager::inst()->value( "audiojack", "clientname" ); if( cn.isEmpty() ) diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index 81cbfc05d..05dbf560b 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -74,11 +74,11 @@ -AudioOss::AudioOss( bool & _success_ful, mixer * _mixer ) : - AudioDevice( tLimit( +AudioOss::AudioOss( bool & _success_ful, AudioOutputContext * context ) : + AudioBackend( tLimit( configManager::inst()->value( "audiooss", "channels" ).toInt(), DEFAULT_CHANNELS, SURROUND_CHANNELS ), - _mixer ), + context ), m_convertEndian( false ) { _success_ful = false; @@ -106,7 +106,7 @@ AudioOss::AudioOss( bool & _success_ful, mixer * _mixer ) : int frag_spec; for( frag_spec = 0; static_cast( 0x01 << frag_spec ) < - getMixer()->framesPerPeriod() * channels() * + mixer()->framesPerPeriod() * channels() * BYTES_PER_INT_SAMPLE; ++frag_spec ) { @@ -178,7 +178,7 @@ AudioOss::AudioOss( bool & _success_ful, mixer * _mixer ) : } if( value != sampleRate() ) { - value = getMixer()->baseSampleRate(); + value = mixer()->baseSampleRate(); if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 ) { perror( "SNDCTL_DSP_SPEED" ); @@ -271,7 +271,7 @@ void AudioOss::applyQualitySettings() { if( hqAudio() ) { - setSampleRate( engine::getMixer()->processingSampleRate() ); + setSampleRate( mixer()->processingSampleRate() ); unsigned int value = sampleRate(); if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 ) @@ -282,7 +282,7 @@ void AudioOss::applyQualitySettings() } if( value != sampleRate() ) { - value = getMixer()->baseSampleRate(); + value = mixer()->baseSampleRate(); if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 ) { perror( "SNDCTL_DSP_SPEED" ); @@ -292,8 +292,6 @@ void AudioOss::applyQualitySettings() setSampleRate( value ); } } - - AudioDevice::applyQualitySettings(); } @@ -302,10 +300,10 @@ void AudioOss::applyQualitySettings() void AudioOss::run() { sampleFrameA * temp = CPU::allocFrames( - getMixer()->framesPerPeriod() ); + mixer()->framesPerPeriod() ); intSampleFrameA * outbuf = (intSampleFrameA *) CPU::memAlloc( sizeof( intSampleFrameA ) * - getMixer()->framesPerPeriod() ); + mixer()->framesPerPeriod() ); while( true ) { @@ -316,7 +314,7 @@ void AudioOss::run() } int bytes = CPU::convertToS16( temp, outbuf, frames, - getMixer()->masterGain(), + mixer()->masterGain(), m_convertEndian ); if( write( m_audioFD, outbuf, bytes ) != bytes ) { @@ -332,7 +330,7 @@ void AudioOss::run() AudioOss::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioOss::name(), _parent ) + AudioBackend::setupWidget( AudioOss::name(), _parent ) { m_device = new QLineEdit( probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); diff --git a/src/core/audio/AudioOutputContext.cpp b/src/core/audio/AudioOutputContext.cpp new file mode 100644 index 000000000..ae3187e60 --- /dev/null +++ b/src/core/audio/AudioOutputContext.cpp @@ -0,0 +1,311 @@ +/* + * AudioOutputContext.cpp - centralize all audio output related functionality + * + * Copyright (c) 2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "AudioBackend.h" +#include "AudioOutputContext.h" +#include "Cpu.h" + +#include "config_mgr.h" +#include "engine.h" + +AudioOutputContext::BufferFifo::BufferFifo( int _size, int _bufferSize ) : + m_readerSem( _size ), + m_writerSem( _size ), + m_readerIndex( 0 ), + m_writerIndex( 0 ), + m_size( _size ), + m_bufferSize( _bufferSize ) +{ + m_buffers = new sampleFrameA *[m_size]; + for( int i = 0; i < m_size; ++i ) + { + m_buffers[i] = CPU::allocFrames( m_bufferSize ); + } + + m_bufferStates = new BufferState[m_size]; + + m_readerSem.acquire( _size ); +} + + + +AudioOutputContext::BufferFifo::~BufferFifo() +{ + for( int i = 0; i < m_size; ++i ) + { + CPU::freeFrames( m_buffers[i] ); + } + + delete[] m_buffers; + delete[] m_bufferStates; + + m_readerSem.release( m_size ); +} + + + + +void AudioOutputContext::BufferFifo::write( sampleFrameA * _buffer ) +{ + m_writerSem.acquire(); + + if( _buffer != NULL ) + { + CPU::memCpy( m_buffers[m_writerIndex], _buffer, + m_bufferSize * sizeof( sampleFrameA ) ); + m_bufferStates[m_writerIndex] = Running; + } + else + { + m_bufferStates[m_writerIndex] = NullBuffer; + } + + m_writerIndex = ( m_writerIndex + 1 ) % m_size; + + m_readerSem.release(); +} + + + + +void AudioOutputContext::BufferFifo::startRead() +{ + m_readerSem.acquire(); +} + + + + +void AudioOutputContext::BufferFifo::finishRead() +{ + m_readerIndex = ( m_readerIndex + 1 ) % m_size; + m_writerSem.release(); +} + + + + + + + +AudioOutputContext::AudioOutputContext( Mixer * mixer, + AudioBackend * audioBackend, + const QualitySettings & qualitySettings ) : + m_mixer( mixer ), + m_qualitySettings( qualitySettings ), + m_audioBackend( audioBackend ), + m_fifo( NULL ), + m_fifoWriter( NULL ) +{ + int error; + if( ( m_srcState = src_new( + qualitySettings.libsrcInterpolation(), + SURROUND_CHANNELS, &error ) ) == NULL ) + { + qWarning( "src_new() failed in AudioOutputContext::AudioOutputContext()" ); + } + //m_audioBackend->applyQualitySettings(); + + int framesPerPeriod = m_mixer->framesPerPeriod(); + + // just rendering? + if( !engine::hasGUI() ) + { + m_fifo = new BufferFifo( 1, framesPerPeriod ); + } + else if( configManager::inst()->value( "mixer", "framesperaudiobuffer" + ).toInt() >= 32 ) + { + framesPerPeriod = + (fpp_t) configManager::inst()->value( "mixer", + "framesperaudiobuffer" ).toInt(); + + if( framesPerPeriod > DEFAULT_BUFFER_SIZE ) + { + m_fifo = new BufferFifo( framesPerPeriod / DEFAULT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE ); + } + else + { + m_fifo = new BufferFifo( 1, framesPerPeriod ); + } + } + else + { + configManager::inst()->setValue( "mixer", + "framesperaudiobuffer", + QString::number( framesPerPeriod ) ); + m_fifo = new BufferFifo( 1, framesPerPeriod ); + } + +} + + + + +AudioOutputContext::~AudioOutputContext() +{ + while( m_fifo->isEmpty() == false ) + { + m_fifo->startRead(); + m_fifo->finishRead(); + } + delete m_fifo; + + src_delete( m_srcState ); +} + + + + +void AudioOutputContext::startProcessing() +{ + if( !isProcessing() ) + { + m_fifoWriter = new FifoWriter( this ); + m_fifoWriter->start( QThread::HighPriority ); + + m_audioBackend->startProcessing(); + } +} + + + + +void AudioOutputContext::stopProcessing() +{ + if( isProcessing() ) + { + m_fifoWriter->finish(); + m_audioBackend->stopProcessing(); + m_fifoWriter->wait(); + + delete m_fifoWriter; + m_fifoWriter = NULL; + } +} + + + + +bool AudioOutputContext::isProcessing() const +{ + return m_fifoWriter && m_fifoWriter->isRunning(); +} + + + + +int AudioOutputContext::getCurrentOutputBuffer( sampleFrameA * _destBuf, + sample_rate_t _destSampleRate ) +{ + int frames = mixer()->framesPerPeriod(); + m_fifo->startRead(); + if( m_fifo->currentReadBufferState() == BufferFifo::NullBuffer ) + { + m_fifo->finishRead(); + return 0; + } + sampleFrameA * srcBuf = m_fifo->currentReadBuffer(); + + if( mixer()->processingSampleRate() != _destSampleRate ) + { + if( m_srcState == NULL ) + { + m_fifo->finishRead(); + return 0; + } + m_srcData.input_frames = frames; + m_srcData.output_frames = frames; + m_srcData.data_in = (float *) srcBuf; + m_srcData.data_out = (float *) _destBuf; + m_srcData.src_ratio = (double) _destSampleRate / + mixer()->processingSampleRate(); + m_srcData.end_of_input = 0; + int error; + if( ( error = src_process( m_srcState, &m_srcData ) ) ) + { + qWarning( "AudioBackend::resample(): error while resampling: %s", + src_strerror( error ) ); + } + frames = frames * _destSampleRate / mixer()->processingSampleRate(); + } + else + { + CPU::memCpy( _destBuf, srcBuf, frames * sizeof( sampleFrameA ) ); + } + + // tell BufferFifo to release current read buffer + m_fifo->finishRead(); + + return frames; +} + + + + + + +AudioOutputContext::FifoWriter::FifoWriter( AudioOutputContext * context ) : + m_context( context ), + m_writing( true ) +{ +} + + + + +void AudioOutputContext::FifoWriter::finish() +{ + m_writing = false; +} + + + + +void AudioOutputContext::FifoWriter::run() +{ +#if 0 +#ifdef LMMS_BUILD_LINUX +#ifdef LMMS_HAVE_PTHREAD_H + cpu_set_t mask; + CPU_ZERO( &mask ); + CPU_SET( 0, &mask ); + pthread_setaffinity_np( pthread_self(), sizeof( mask ), &mask ); +#endif +#endif +#endif + + while( m_writing ) + { + m_context->fifo()->write( m_context->mixer()->renderNextBuffer() ); + } + + // write a NULL in order to signal the AudioBackend that the FifoWriter has + // finished + m_context->fifo()->write( NULL ); +} + + + diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 2b4301178..8129fbfb0 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -23,7 +23,8 @@ */ #include "AudioPort.h" -#include "AudioDevice.h" +#include "AudioBackend.h" +#include "AudioOutputContext.h" #include "EffectChain.h" #include "engine.h" #include "Cpu.h" @@ -88,11 +89,13 @@ void AudioPort::setExtOutputEnabled( bool _enabled ) m_extOutputEnabled = _enabled; if( m_extOutputEnabled ) { - engine::getMixer()->audioDev()->registerPort( this ); + engine::mixer()->audioOutputContext()-> + audioBackend()->registerPort( this ); } else { - engine::getMixer()->audioDev()->unregisterPort( this ); + engine::mixer()->audioOutputContext()-> + audioBackend()->unregisterPort( this ); } } } @@ -103,7 +106,7 @@ void AudioPort::setExtOutputEnabled( bool _enabled ) void AudioPort::setName( const QString & _name ) { m_name = _name; - engine::getMixer()->audioDev()->renamePort( this ); + engine::mixer()->audioOutputContext()->audioBackend()->renamePort( this ); } diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index 3e0713252..e2742d9b5 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -50,7 +50,7 @@ void AudioPortAudioSetupUtil::updateChannels() AudioPortAudio::AudioPortAudio( bool & _success_ful, mixer * _mixer ) : - AudioDevice( tLimit( + AudioBackend( tLimit( configManager::inst()->value( "audioportaudio", "channels" ).toInt(), DEFAULT_CHANNELS, SURROUND_CHANNELS ), @@ -284,8 +284,6 @@ void AudioPortAudio::applyQualitySettings() return; } } - - audioDevice::applyQualitySettings(); } int AudioPortAudio::process_callback( diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index 539b1fba5..6bc621b20 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -46,11 +46,11 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) -AudioPulseAudio::AudioPulseAudio( bool & _success_ful, mixer * _mixer ) : - AudioDevice( tLimit( +AudioPulseAudio::AudioPulseAudio( bool & _success_ful, AudioOutputContext * context ) : + AudioBackend( tLimit( configManager::inst()->value( "audiopa", "channels" ).toInt(), DEFAULT_CHANNELS, SURROUND_CHANNELS ), - _mixer ), + context ), m_s( NULL ), m_quit( false ), m_convertEndian( false ) @@ -119,11 +119,9 @@ void AudioPulseAudio::applyQualitySettings() { if( hqAudio() ) { -// setSampleRate( engine::getMixer()->processingSampleRate() ); +// setSampleRate( mixer()->processingSampleRate() ); } - - AudioDevice::applyQualitySettings(); } @@ -139,12 +137,12 @@ static void stream_state_callback( pa_stream *s, void * userdata ) break; case PA_STREAM_READY: - qDebug( "Stream successfully created\n" ); + qDebug( "Stream successfully created" ); break; case PA_STREAM_FAILED: default: - qCritical( "Stream errror: %s\n", + qCritical( "Stream errror: %s", pa_strerror(pa_context_errno( pa_stream_get_context( s ) ) ) ); } @@ -166,7 +164,7 @@ static void context_state_callback(pa_context *c, void *userdata) case PA_CONTEXT_READY: { pa_cvolume cv; - qDebug( "Connection established.\n" ); + qDebug( "Connection established." ); _this->m_s = pa_stream_new( c, "lmms", &_this->m_sampleSpec, NULL); pa_stream_set_state_callback( _this->m_s, stream_state_callback, _this ); pa_stream_set_write_callback( _this->m_s, stream_write_callback, _this ); @@ -181,7 +179,8 @@ static void context_state_callback(pa_context *c, void *userdata) buffer_attr.minreq = (uint32_t)(-1); buffer_attr.fragsize = (uint32_t)(-1); - double latency = (double)( engine::getMixer()->framesPerPeriod() ) / + double latency = (double)( ( (const AudioPulseAudio *) _this )-> + mixer()->framesPerPeriod() ) / (double)_this->sampleRate(); // ask PulseAudio for the desired latency (which might not be approved) @@ -201,7 +200,7 @@ static void context_state_callback(pa_context *c, void *userdata) case PA_CONTEXT_FAILED: default: - qCritical( "Connection failure: %s\n", pa_strerror( pa_context_errno( c ) ) ); + qCritical( "Connection failure: %s", pa_strerror( pa_context_errno( c ) ) ); } } @@ -213,7 +212,7 @@ void AudioPulseAudio::run() pa_mainloop * mainLoop = pa_mainloop_new(); if( !mainLoop ) { - qCritical( "pa_mainloop_new() failed.\n" ); + qCritical( "pa_mainloop_new() failed." ); return; } pa_mainloop_api * mainloop_api = pa_mainloop_get_api( mainLoop ); @@ -250,7 +249,7 @@ void AudioPulseAudio::run() void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length ) { - const fpp_t fpp = getMixer()->framesPerPeriod(); + const fpp_t fpp = mixer()->framesPerPeriod(); sampleFrameA * temp = CPU::allocFrames( fpp ); Sint16 * pcmbuf = (Sint16*)CPU::memAlloc( fpp * channels() * sizeof(Sint16) ); @@ -267,7 +266,7 @@ void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length ) int bytes = CPU::convertToS16( temp, (intSampleFrameA *) pcmbuf, frames, - getMixer()->masterGain(), + mixer()->masterGain(), m_convertEndian ); if( bytes > 0 ) { @@ -285,7 +284,7 @@ void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length ) AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioPulseAudio::name(), _parent ) + AudioBackend::setupWidget( AudioPulseAudio::name(), _parent ) { m_device = new QLineEdit( AudioPulseAudio::probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); diff --git a/src/core/audio/AudioSampleRecorder.cpp b/src/core/audio/AudioSampleRecorder.cpp index 4601182a5..d280c0468 100644 --- a/src/core/audio/AudioSampleRecorder.cpp +++ b/src/core/audio/AudioSampleRecorder.cpp @@ -26,14 +26,14 @@ #include "AudioSampleRecorder.h" #include "sample_buffer.h" -#include "debug.h" +#include "Cpu.h" AudioSampleRecorder::AudioSampleRecorder( const ch_cnt_t _channels, bool & _success_ful, - mixer * _mixer ) : - AudioDevice( _channels, _mixer ), + AudioOutputContext * context ) : + AudioBackend( _channels, context ), m_buffers() { _success_ful = true; @@ -46,7 +46,7 @@ AudioSampleRecorder::~AudioSampleRecorder() { while( !m_buffers.empty() ) { - delete[] m_buffers.front().first; + CPU::freeFrames( m_buffers.front().first ); m_buffers.erase( m_buffers.begin() ); } } @@ -72,9 +72,9 @@ void AudioSampleRecorder::createSampleBuffer( sampleBuffer * * _sample_buf ) { const f_cnt_t frames = framesRecorded(); // create buffer to store all recorded buffers in - sampleFrame * data = new sampleFrame[frames]; + sampleFrameA * data = CPU::allocFrames( frames ); // make sure buffer is cleaned up properly at the end... - sampleFrame * data_ptr = data; + sampleFrameA * data_ptr = data; #ifdef LMMS_DEBUG assert( data != NULL ); @@ -83,30 +83,24 @@ void AudioSampleRecorder::createSampleBuffer( sampleBuffer * * _sample_buf ) for( BufferList::ConstIterator it = m_buffers.begin(); it != m_buffers.end(); ++it ) { - memcpy( data_ptr, ( *it ).first, ( *it ).second * - sizeof( sampleFrame ) ); + CPU::memCpy( data_ptr, ( *it ).first, ( *it ).second * + sizeof( sampleFrameA ) ); data_ptr += ( *it ).second; } // create according sample-buffer out of big buffer *_sample_buf = new sampleBuffer( data, frames ); ( *_sample_buf )->setSampleRate( sampleRate() ); - delete[] data; + CPU::freeFrames( data ); } -void AudioSampleRecorder::writeBuffer( const surroundSampleFrame * _ab, +void AudioSampleRecorder::writeBuffer( const sampleFrameA * srcBuf, const fpp_t _frames, const float ) { - sampleFrame * buf = new sampleFrame[_frames]; - for( fpp_t frame = 0; frame < _frames; ++frame ) - { - for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) - { - buf[frame][chnl] = _ab[frame][chnl]; - } - } + sampleFrameA * buf = CPU::allocFrames( _frames ); + CPU::memCpy( buf, srcBuf, _frames*sizeof( sampleFrameA ) ); m_buffers.push_back( qMakePair( buf, _frames ) ); } diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 78affdb9a..bc62ffee0 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -38,16 +38,16 @@ -AudioSdl::AudioSdl( bool & _success_ful, mixer * _mixer ) : - AudioDevice( DEFAULT_CHANNELS, _mixer ), - m_outBuf( CPU::allocFrames( getMixer()->framesPerPeriod() ) ), +AudioSdl::AudioSdl( bool & _success_ful, AudioOutputContext * context ) : + AudioBackend( DEFAULT_CHANNELS, context ), + m_outBuf( CPU::allocFrames( mixer()->framesPerPeriod() ) ), m_convertedBufPos( 0 ), m_convertEndian( false ), m_stopSemaphore( 1 ) { _success_ful = false; - m_convertedBufSize = getMixer()->framesPerPeriod() * + m_convertedBufSize = mixer()->framesPerPeriod() * sizeof( intSampleFrameA ); m_convertedBuf = (intSampleFrameA *) CPU::memAlloc( m_convertedBufSize ); @@ -63,7 +63,7 @@ AudioSdl::AudioSdl( bool & _success_ful, mixer * _mixer ) : // of system, so we don't have // to convert the buffers m_audioHandle.channels = channels(); - m_audioHandle.samples = qMax( 1024, getMixer()->framesPerPeriod()*2 ); + m_audioHandle.samples = qMax( 1024, mixer()->framesPerPeriod()*2 ); m_audioHandle.callback = sdlAudioCallback; m_audioHandle.userdata = this; @@ -131,7 +131,7 @@ void AudioSdl::applyQualitySettings() { SDL_CloseAudio(); - setSampleRate( engine::getMixer()->processingSampleRate() ); + setSampleRate( mixer()->processingSampleRate() ); m_audioHandle.freq = sampleRate(); @@ -143,8 +143,6 @@ void AudioSdl::applyQualitySettings() qCritical( "Couldn't open SDL-audio: %s\n", SDL_GetError() ); } } - - AudioDevice::applyQualitySettings(); } @@ -186,7 +184,7 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) CPU::convertToS16( m_outBuf, m_convertedBuf, frames, - getMixer()->masterGain(), + mixer()->masterGain(), m_convertEndian ); } const int min_len = qMin( _len, m_convertedBufSize @@ -203,7 +201,7 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioSdl::name(), _parent ) + AudioBackend::setupWidget( AudioSdl::name(), _parent ) { QString dev = configManager::inst()->value( "audiosdl", "device" ); m_device = new QLineEdit( dev, this ); diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 6606619c9..0bb323b5e 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -37,7 +37,7 @@ #include "InstrumentTrack.h" #include "ladspa_2_lmms.h" #include "MainWindow.h" -#include "mixer.h" +#include "Mixer.h" #include "pattern.h" #include "piano_roll.h" #include "ProjectJournal.h" @@ -56,7 +56,7 @@ bool engine::s_hasGUI = true; bool engine::s_suppressMessages = false; float engine::s_framesPerTick; -mixer * engine::s_mixer = NULL; +Mixer * engine::s_mixer = NULL; FxMixer * engine::s_fxMixer = NULL; FxMixerView * engine::s_fxMixerView = NULL; MainWindow * engine::s_mainWindow = NULL; @@ -89,9 +89,11 @@ void engine::init( const bool _has_gui ) initPluginFileHandling(); s_projectJournal = new ProjectJournal; - s_mixer = new mixer; + s_mixer = new Mixer; + s_song = new song; + s_mixer->initDevices(); // init resource framework s_workingDirResourceDB = @@ -119,8 +121,6 @@ void engine::init( const bool _has_gui ) s_projectJournal->setJournalling( true ); - s_mixer->initDevices(); - s_midiControlListener = new MidiControlListener(); s_automationRecorder = new AutomationRecorder; diff --git a/src/core/main.cpp b/src/core/main.cpp index 41227025f..ec5b46b63 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -126,7 +126,8 @@ int main( int argc, char * * argv ) new QApplication( argc, argv ) ; - mixer::qualitySettings qs( mixer::qualitySettings::Mode_HighQuality ); + AudioOutputContext::QualitySettings qs( + AudioOutputContext::QualitySettings::Preset_HighQuality ); ProjectRenderer::OutputSettings os( 44100, false, 160, ProjectRenderer::Depth_16Bit ); ProjectRenderer::ExportFileFormats eff = ProjectRenderer::WaveFile; @@ -284,19 +285,23 @@ int main( int argc, char * * argv ) const QString ip = QString( argv[i + 1] ); if( ip == "linear" ) { - qs.interpolation = mixer::qualitySettings::Interpolation_Linear; + qs.setInterpolation( AudioOutputContext::QualitySettings:: + Interpolation_Linear ); } else if( ip == "sincfastest" ) { - qs.interpolation = mixer::qualitySettings::Interpolation_SincFastest; + qs.setInterpolation( AudioOutputContext::QualitySettings:: + Interpolation_SincFastest ); } else if( ip == "sincmedium" ) { - qs.interpolation = mixer::qualitySettings::Interpolation_SincMedium; + qs.setInterpolation( AudioOutputContext::QualitySettings:: + Interpolation_SincMedium ); } else if( ip == "sincbest" ) { - qs.interpolation = mixer::qualitySettings::Interpolation_SincBest; + qs.setInterpolation( AudioOutputContext::QualitySettings:: + Interpolation_SincBest ); } else { @@ -314,21 +319,25 @@ int main( int argc, char * * argv ) switch( o ) { case 1: - qs.oversampling = mixer::qualitySettings::Oversampling_None; - break; + qs.setOversampling( AudioOutputContext::QualitySettings:: + Oversampling_None ); + break; case 2: - qs.oversampling = mixer::qualitySettings::Oversampling_2x; - break; + qs.setOversampling( AudioOutputContext::QualitySettings:: + Oversampling_2x ); + break; case 4: - qs.oversampling = mixer::qualitySettings::Oversampling_4x; - break; + qs.setOversampling( AudioOutputContext::QualitySettings:: + Oversampling_4x ); + break; case 8: - qs.oversampling = mixer::qualitySettings::Oversampling_8x; - break; + qs.setOversampling( AudioOutputContext::QualitySettings:: + Oversampling_8x ); + break; default: - printf( "\nInvalid oversampling %s.\n\n" + printf( "\nInvalid oversampling %s.\n\n" "Try \"%s --help\" for more information.\n\n", argv[i + 1], argv[0] ); - return( EXIT_FAILURE ); + return EXIT_FAILURE; } ++i; } diff --git a/src/core/midi/MidiControlListener.cpp b/src/core/midi/MidiControlListener.cpp index f014cfc56..4a089f888 100644 --- a/src/core/midi/MidiControlListener.cpp +++ b/src/core/midi/MidiControlListener.cpp @@ -30,7 +30,6 @@ #include #include "MidiControlListener.h" -#include "mixer.h" #include "MidiClient.h" #include "MidiPort.h" #include "engine.h" diff --git a/src/core/midi/MidiController.cpp b/src/core/midi/MidiController.cpp index ba74a9e62..5cb60c61e 100644 --- a/src/core/midi/MidiController.cpp +++ b/src/core/midi/MidiController.cpp @@ -29,7 +29,6 @@ #include "song.h" #include "engine.h" -#include "mixer.h" #include "MidiClient.h" #include "MidiController.h" #include "automation_recorder.h" diff --git a/src/core/sample_buffer.cpp b/src/core/sample_buffer.cpp index 5958ea82c..4051524fa 100644 --- a/src/core/sample_buffer.cpp +++ b/src/core/sample_buffer.cpp @@ -24,7 +24,7 @@ #include "sample_buffer.h" -#include "mixer.h" +#include "Mixer.h" #include diff --git a/src/gui/ExportProjectDialog.cpp b/src/gui/ExportProjectDialog.cpp index 6ac5f6d7f..b8c8ccf8b 100644 --- a/src/gui/ExportProjectDialog.cpp +++ b/src/gui/ExportProjectDialog.cpp @@ -146,11 +146,12 @@ void ExportProjectDialog::startBtnClicked() ui->progressBar->setEnabled( true ); - mixer::qualitySettings qs = mixer::qualitySettings( - static_cast( - ui->interpolationCB->currentIndex() ), - static_cast( - ui->oversamplingCB->currentIndex() ), + AudioOutputContext::QualitySettings qs = + AudioOutputContext::QualitySettings( + static_cast( + ui->interpolationCB->currentIndex() ), + static_cast( + ui->oversamplingCB->currentIndex() ), ui->sampleExactControllersCB->isChecked(), ui->aliasFreeOscillatorsCB->isChecked() ); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 453193dbd..307e96bf3 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -56,7 +56,7 @@ #include "plugin_browser.h" #include "SideBar.h" #include "config_mgr.h" -#include "mixer.h" +#include "Mixer.h" #include "project_notes.h" #include "setup_dialog.h" #include "AudioDummy.h" @@ -1470,9 +1470,9 @@ void MainWindow::browseHelp() void MainWindow::setHighQuality( bool _hq ) { - engine::getMixer()->changeQuality( mixer::qualitySettings( - _hq ? mixer::qualitySettings::Mode_HighQuality : - mixer::qualitySettings::Mode_Draft ) ); + /*engine::getMixer()->changeQuality( Mixer::qualitySettings( + _hq ? Mixer::qualitySettings::Mode_HighQuality : + Mixer::qualitySettings::Mode_Draft ) );*/ } diff --git a/src/gui/setup_dialog.cpp b/src/gui/setup_dialog.cpp index 10d783f69..05c813101 100644 --- a/src/gui/setup_dialog.cpp +++ b/src/gui/setup_dialog.cpp @@ -39,7 +39,6 @@ #include "tab_button.h" #include "tab_widget.h" #include "gui_templates.h" -#include "mixer.h" #include "ProjectJournal.h" #include "config_mgr.h" #include "embed.h" diff --git a/src/gui/song_editor.cpp b/src/gui/song_editor.cpp index 5bf826549..697b2b8af 100644 --- a/src/gui/song_editor.cpp +++ b/src/gui/song_editor.cpp @@ -35,6 +35,7 @@ #include +#include "AudioOutputContext.h" #include "song_editor.h" #include "combobox.h" #include "embed.h" @@ -43,7 +44,7 @@ #include "timeline.h" #include "tool_button.h" #include "tooltip.h" -#include "AudioDevice.h" +#include "AudioBackend.h" #include "piano_roll.h" @@ -130,7 +131,7 @@ songEditor::songEditor( song * _song, songEditor * & _engine_ptr ) : m_recordButton->setDisabled( true ); // disable record buttons if capturing is not supported - if( !engine::getMixer()->audioDev()->supportsCapture() ) + if( !engine::mixer()->audioOutputContext()->audioBackend()->supportsCapture() ) { m_recordButton->setDisabled( true ); m_recordAccompanyButton->setDisabled( true ); diff --git a/src/gui/widgets/EnvelopeAndLfoView.cpp b/src/gui/widgets/EnvelopeAndLfoView.cpp index cd68ad36c..dbc1f48c7 100644 --- a/src/gui/widgets/EnvelopeAndLfoView.cpp +++ b/src/gui/widgets/EnvelopeAndLfoView.cpp @@ -33,7 +33,7 @@ #include "gui_templates.h" #include "knob.h" #include "led_checkbox.h" -#include "mixer.h" +#include "Mixer.h" #include "mmp.h" #include "Oscillator.h" #include "pixmap_button.h" @@ -482,7 +482,7 @@ void EnvelopeAndLfoView::paintEvent( QPaintEvent * ) int graphYBase = LFO_GRAPH_Y + 3 + lfoGraphHeight / 2; const float framesForGraph = SECS_PER_LFO_OSCILLATION * - engine::getMixer()->baseSampleRate() / 10; + engine::mixer()->baseSampleRate() / 10; const float lfoGrayAmount = fabsf( m_lfoAmountKnob->value() ); p.setPen( QPen( QColor::fromHsvF( diff --git a/src/gui/widgets/InstrumentMidiIOView.cpp b/src/gui/widgets/InstrumentMidiIOView.cpp index 7db26499b..d8572a03d 100644 --- a/src/gui/widgets/InstrumentMidiIOView.cpp +++ b/src/gui/widgets/InstrumentMidiIOView.cpp @@ -33,7 +33,7 @@ #include "gui_templates.h" #include "lcd_spinbox.h" #include "MidiClient.h" -#include "mixer.h" +#include "Mixer.h" #include "tooltip.h" @@ -93,7 +93,7 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget * _parent ) : m_outputProgramSpinBox, SLOT( setEnabled( bool ) ) ); - if( !engine::getMixer()->midiClient()->isRaw() ) + if( !engine::mixer()->midiClient()->isRaw() ) { m_rpBtn = new QToolButton( m_midiInputGroupBox ); m_rpBtn->setText( tr( "MIDI devices to receive MIDI events from" ) ); diff --git a/src/gui/widgets/cpuload_widget.cpp b/src/gui/widgets/cpuload_widget.cpp index 5856bfd6f..7b4db3a29 100644 --- a/src/gui/widgets/cpuload_widget.cpp +++ b/src/gui/widgets/cpuload_widget.cpp @@ -2,8 +2,8 @@ * cpuload_widget.cpp - widget for displaying CPU-load (partly based on * Hydrogen's CPU-load-widget) * - * Copyright (c) 2005-2007 Tobias Doerffel - * + * Copyright (c) 2005-2009 Tobias Doerffel + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or @@ -29,7 +29,7 @@ #include "cpuload_widget.h" #include "embed.h" #include "engine.h" -#include "mixer.h" +#include "Mixer.h" cpuloadWidget::cpuloadWidget( QWidget * _parent ) : @@ -90,7 +90,7 @@ void cpuloadWidget::paintEvent( QPaintEvent * ) void cpuloadWidget::updateCpuLoad() { // smooth load-values a bit - Uint8 new_load = ( m_currentLoad + engine::getMixer()->cpuLoad() ) / 2; + int new_load = ( m_currentLoad + engine::mixer()->cpuLoad() ) / 2; if( new_load != m_currentLoad ) { m_currentLoad = new_load; diff --git a/src/gui/widgets/visualization_widget.cpp b/src/gui/widgets/visualization_widget.cpp index dc017300f..e1de1d384 100644 --- a/src/gui/widgets/visualization_widget.cpp +++ b/src/gui/widgets/visualization_widget.cpp @@ -29,6 +29,7 @@ #include "visualization_widget.h" #include "gui_templates.h" #include "MainWindow.h" +#include "Mixer.h" #include "embed.h" #include "engine.h" #include "tooltip.h" @@ -40,16 +41,16 @@ visualizationWidget::visualizationWidget( const QPixmap & _bg, QWidget * _p, visualizationTypes _vtype ) : QWidget( _p ), s_background( _bg ), - m_points( new QPointF[engine::getMixer()->framesPerPeriod()] ), + m_points( new QPointF[engine::mixer()->framesPerPeriod()] ), m_active( false ) { setFixedSize( s_background.width(), s_background.height() ); setAttribute( Qt::WA_OpaquePaintEvent, true ); - const fpp_t frames = engine::getMixer()->framesPerPeriod(); + const fpp_t frames = engine::mixer()->framesPerPeriod(); m_buffer = new sampleFrame[frames]; - engine::getMixer()->clearAudioBuffer( m_buffer, frames ); + engine::mixer()->clearAudioBuffer( m_buffer, frames ); toolTip::add( this, tr( "click to enable/disable visualization of " @@ -72,12 +73,11 @@ void visualizationWidget::updateAudioBuffer() { if( !engine::getSong()->isExporting() ) { - engine::getMixer()->lock(); - const surroundSampleFrame * c = engine::getMixer()-> - currentReadBuffer(); - const fpp_t fpp = engine::getMixer()->framesPerPeriod(); + engine::mixer()->lock(); + const surroundSampleFrame * c = engine::mixer()->currentReadBuffer(); + const fpp_t fpp = engine::mixer()->framesPerPeriod(); memcpy( m_buffer, c, sizeof( surroundSampleFrame ) * fpp ); - engine::getMixer()->unlock(); + engine::mixer()->unlock(); } } @@ -92,7 +92,7 @@ void visualizationWidget::setActive( bool _active ) connect( engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); - connect( engine::getMixer(), + connect( engine::mixer(), SIGNAL( nextAudioBuffer() ), this, SLOT( updateAudioBuffer() ) ); } @@ -101,7 +101,7 @@ void visualizationWidget::setActive( bool _active ) disconnect( engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); - disconnect( engine::getMixer(), + disconnect( engine::mixer(), SIGNAL( nextAudioBuffer() ), this, SLOT( updateAudioBuffer() ) ); // we have to update (remove last waves), @@ -121,7 +121,7 @@ void visualizationWidget::paintEvent( QPaintEvent * ) if( m_active && !engine::getSong()->isExporting() ) { - float master_output = engine::getMixer()->masterGain(); + float master_output = engine::mixer()->masterGain(); int w = width()-4; const float half_h = -( height() - 6 ) / 3.0 * master_output - 1; int x_base = 2; @@ -131,10 +131,10 @@ void visualizationWidget::paintEvent( QPaintEvent * ) const fpp_t frames = - engine::getMixer()->framesPerPeriod(); + engine::mixer()->framesPerPeriod(); const float max_level = qMax( - mixer::peakValueLeft( m_buffer, frames ), - mixer::peakValueRight( m_buffer, frames ) ); + Mixer::peakValueLeft( m_buffer, frames ), + Mixer::peakValueRight( m_buffer, frames ) ); // and set color according to that... LmmsStyle::ColorRole levelColor; @@ -165,7 +165,7 @@ void visualizationWidget::paintEvent( QPaintEvent * ) { m_points[frame] = QPointF( x_base + (float) frame * xd, - y_base + ( mixer::clip( + y_base + ( Mixer::clip( m_buffer[frame][ch] ) * half_h ) ); } diff --git a/src/tracks/bb_track.cpp b/src/tracks/bb_track.cpp index 11a933bb2..96e920efa 100644 --- a/src/tracks/bb_track.cpp +++ b/src/tracks/bb_track.cpp @@ -33,7 +33,7 @@ #include "embed.h" #include "engine.h" #include "gui_templates.h" -#include "mixer.h" +#include "Mixer.h" #include "rename_dialog.h" #include "song.h" #include "song_editor.h" diff --git a/src/tracks/pattern.cpp b/src/tracks/pattern.cpp index 35a7e42d5..e713a6f90 100644 --- a/src/tracks/pattern.cpp +++ b/src/tracks/pattern.cpp @@ -33,6 +33,7 @@ #include #include +#include "AudioOutputContext.h" #include "pattern.h" #include "InstrumentTrack.h" #include "templates.h" @@ -735,15 +736,14 @@ patternFreezeThread::~patternFreezeThread() void patternFreezeThread::run() { - // create and install audio-sample-recorder + AudioOutputContext context( engine::mixer(), NULL, + engine::mixer()->defaultAudioOutputContext()->qualitySettings() ); + + // create and install AudioSampleRecorder bool b; - // we cannot create local copy, because at a later stage - // mixer::restoreAudioDevice(...) deletes old audio-dev and thus - // AudioSampleRecorder would be destroyed two times... - AudioSampleRecorder * freeze_recorder = new AudioSampleRecorder( - DEFAULT_CHANNELS, b, - engine::getMixer() ); - engine::getMixer()->setAudioDevice( freeze_recorder ); + AudioSampleRecorder freezeRecorder( DEFAULT_CHANNELS, b, &context ); + context.setAudioBackend( &freezeRecorder ); + engine::mixer()->setAudioOutputContext( &context ); // prepare stuff for playing correct things later engine::getSong()->playPattern( m_pattern, false ); @@ -761,7 +761,7 @@ void patternFreezeThread::run() while( ppp < m_pattern->length() && m_pattern->m_freezeAborted == false ) { - freeze_recorder->processNextBuffer(); + freezeRecorder.processNextBuffer(); m_statusDlg->setProgress( ppp * 100 / m_pattern->length() ); } m_statusDlg->setProgress( 100 ); @@ -769,7 +769,7 @@ void patternFreezeThread::run() while( engine::getMixer()->hasPlayHandles() && m_pattern->m_freezeAborted == false ) { - freeze_recorder->processNextBuffer(); + freezeRecorder.processNextBuffer(); } @@ -782,12 +782,13 @@ void patternFreezeThread::run() // create final sample-buffer if freezing was successful if( m_pattern->m_freezeAborted == false ) { - freeze_recorder->createSampleBuffer( + freezeRecorder.createSampleBuffer( &m_pattern->m_frozenPattern ); } // restore original audio-device - engine::getMixer()->restoreAudioDevice(); + engine::mixer()->setAudioOutputContext( + engine::mixer()->defaultAudioOutputContext() ); m_statusDlg->setProgress( -1 ); // we're finished