Merge branch 'master' into calf-updates

This commit is contained in:
Tobias Doerffel
2009-12-03 00:18:20 +01:00
133 changed files with 5831 additions and 1912 deletions

1514
Doxyfile Normal file
View File

File diff suppressed because it is too large Load Diff

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -12,6 +12,17 @@ automationEditor {
background-color: rgb(0, 0, 0);
}
#PreferencesDialog QListView::item:selected {
background: qlineargradient(x1: 0, y1:0, x2:0,y2:1, stop:0 rgb(84,87,96), stop:1 rgb(54,57,66));
border-radius: 8px;
color: white;
}
#PreferencesDialog QListView::item:hover:!selected {
background: qlineargradient(x1: 0, y1:0, x2:0,y2:1, stop:0 rgb(104,107,116), stop:1 rgb(94,97,106));
border-radius: 8px;
}
#WelcomeFrame {
border: 2px solid rgb(32,32,32);
border-radius: 8px;

View File

@@ -34,17 +34,17 @@
#include <alsa/asoundlib.h>
#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 );

View File

@@ -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 <tobydox/at/users.sourceforge.net>
*
@@ -26,37 +26,31 @@
#define _AUDIO_DEVICE_H
#include <QtCore/QPair>
#include <QtCore/QMutex>
#include <QtCore/QThread>
#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;

View File

@@ -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<Sint32>(
getMixer()->framesPerPeriod() *
1000000.0f /
getMixer()->processingSampleRate() -
timer.elapsed() );
mixer()->framesPerPeriod() * 1000000.0f /
mixer()->processingSampleRate() - timer.elapsed() );
if( microseconds > 0 )
{
usleep( microseconds );
}
}
CPU::freeFrames( buf );
}
} ;

View File

@@ -28,10 +28,10 @@
#include <QtCore/QFile>
#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

View File

@@ -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 );
}

View File

@@ -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 );
}

View File

@@ -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 );
}

View File

@@ -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 <tobydox/at/users.sourceforge.net>
@@ -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 );
}

View File

@@ -31,22 +31,22 @@
#include <jack/jack.h>
#endif
#include <QtCore/QVector>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QSemaphore>
#include <QtCore/QVector>
#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 );

View File

@@ -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 );

View File

@@ -0,0 +1,377 @@
/*
* AudioOutputContext.h - centralize all audio output related functionality
*
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 <QtCore/QSemaphore>
#include <QtCore/QThread>
#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

View File

@@ -27,13 +27,12 @@
#include <QtCore/QString>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include "mixer.h"
#include "Mixer.h"
class EffectChain;
class AudioPort
class AudioPort : public ThreadableJob
{
public:
AudioPort( const QString & _name, bool _has_effect_chain = true );
@@ -109,6 +108,13 @@ public:
bool processEffects();
// ThreadableJob stuff
virtual void doProcessing( sampleFrame * );
virtual bool requiresProcessing() const
{
return true;
}
enum bufferUsages
{
@@ -134,7 +140,7 @@ private:
EffectChain * m_effects;
friend class mixer;
friend class Mixer;
friend class MixerWorkerThread;
} ;

View File

@@ -47,7 +47,7 @@ public:
#include <portaudio.h>
#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 );

View File

@@ -31,17 +31,17 @@
#include <pulse/pulseaudio.h>
#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 );
@@ -79,6 +79,7 @@ private:
virtual void applyQualitySettings();
virtual void run();
volatile bool m_quit;
bool m_convertEndian;

View File

@@ -29,16 +29,16 @@
#include <QtCore/QList>
#include <QtCore/QPair>
#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<QPair<sampleFrame *, fpp_t> > BufferList;
typedef QList<QPair<sampleFrameA *, fpp_t> > BufferList;
BufferList m_buffers;
} ;

View File

@@ -29,18 +29,20 @@
#ifdef LMMS_HAVE_SDL
#include <QtCore/QSemaphore>
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#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 );

View File

@@ -225,6 +225,9 @@ protected:
float fittedValue( float _value ) const;
float m_minValue;
float m_maxValue;
float m_value;
private:
void linkModel( AutomatableModel * _model );
@@ -232,10 +235,7 @@ private:
DataType m_dataType;
float m_value;
float m_initValue;
float m_minValue;
float m_maxValue;
float m_step;
float m_range;
@@ -280,8 +280,7 @@ signals:
inline type maxValue() const \
{ \
return AutomatableModel::maxValue<type>(); \
} \
}
// some typed AutomatableModel-definitions

View File

@@ -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;
}

View File

@@ -56,6 +56,9 @@ typedef void (*BufApplyGainFunc)( sampleFrameA * RP _dst,
typedef void (*BufMixFunc)( sampleFrameA * RP _dst,
const sampleFrameA * RP _src,
int _frames );
typedef void (*BufMixCoeffFunc)( sampleFrameA * RP _dst,
const sampleFrameA * RP _src,
float _coeff, int _frames );
typedef void (*BufMixLRCoeffFunc)( sampleFrameA * RP _dst,
const sampleFrameA * RP _src,
float _left, float _right,
@@ -81,6 +84,7 @@ extern MemCpyFunc memCpy;
extern MemClearFunc memClear;
extern BufApplyGainFunc bufApplyGain;
extern BufMixFunc bufMix;
extern BufMixCoeffFunc bufMixCoeff;
extern BufMixLRCoeffFunc bufMixLRCoeff;
extern UnalignedBufMixLRCoeffFunc unalignedBufMixLRCoeff;
extern BufWetDryMixFunc bufWetDryMix;

View File

@@ -28,7 +28,7 @@
#include "Plugin.h"
#include "engine.h"
#include "mixer.h"
#include "Mixer.h"
#include "AutomatableModel.h"
#include "TempoSyncKnobModel.h"

View File

@@ -28,7 +28,7 @@
#include "Model.h"
#include "SerializingObject.h"
#include "mixer.h"
#include "Mixer.h"
#include "AutomatableModel.h"
class Effect;

41
include/FxLine.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef FXLINE_H
#define FXLINE_H
#include <QWidget>
#include <QLabel>
#include "knob.h"
#include "lcd_spinbox.h"
#include "SendButtonIndicator.h"
class FxMixerView;
class SendButtonIndicator;
class FxLine : public QWidget
{
Q_OBJECT
public:
FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex);
~FxLine();
virtual void paintEvent( QPaintEvent * );
virtual void mousePressEvent( QMouseEvent * );
virtual void mouseDoubleClickEvent( QMouseEvent * );
inline int channelIndex() { return m_channelIndex; }
void setChannelIndex(int index);
knob * m_sendKnob;
SendButtonIndicator * m_sendBtn;
private:
FxMixerView * m_mv;
lcdSpinBox * m_lcd;
int m_channelIndex;
} ;
#endif // FXLINE_H

View File

@@ -26,31 +26,46 @@
#define _FX_MIXER_H
#include "Model.h"
#include "mixer.h"
#include "Mixer.h"
#include "EffectChain.h"
#include "JournallingObject.h"
const int NumFxChannels = 64;
struct FxChannel
class FxChannel : public ThreadableJob
{
FxChannel( Model * _parent );
~FxChannel();
public:
FxChannel( Model * _parent );
~FxChannel();
EffectChain m_fxChain;
bool m_used;
bool m_stillRunning;
float m_peakLeft;
float m_peakRight;
sampleFrame * m_buffer;
BoolModel m_muteModel;
FloatModel m_volumeModel;
QString m_name;
QMutex m_lock;
EffectChain m_fxChain;
} ;
// set to true if any effect in the channel is enabled and running
bool m_stillRunning;
float m_peakLeft;
float m_peakRight;
sampleFrame * m_buffer;
BoolModel m_muteModel;
FloatModel m_volumeModel;
QString m_name;
QMutex m_lock;
int m_channelIndex; // what channel index are we
bool m_queued; // are we queued up for rendering yet?
// pointers to other channels that this one sends to
QVector<fx_ch_t> m_sends;
QVector<FloatModel *> m_sendAmount;
// pointers to other channels that send to this one
QVector<fx_ch_t> m_receives;
virtual bool requiresProcessing() const { return true; }
private:
virtual void doProcessing( sampleFrame * _working_buffer );
};
@@ -61,14 +76,10 @@ public:
virtual ~FxMixer();
void mixToChannel( const sampleFrame * _buf, fx_ch_t _ch );
void processChannel( fx_ch_t _ch, sampleFrame * _buf = NULL );
void prepareMasterMix();
void masterMix( sampleFrame * _buf );
void clear();
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
virtual void loadSettings( const QDomElement & _this );
@@ -79,17 +90,55 @@ public:
FxChannel * effectChannel( int _ch )
{
if( _ch >= 0 && _ch <= NumFxChannels )
{
return m_fxChannels[_ch];
}
return NULL;
return m_fxChannels[_ch];
}
// make the output of channel fromChannel go to the input of channel toChannel
// it is safe to call even if the send already exists
void createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel,
float amount = 1.0f);
// delete the connection made by createChannelSend
void deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel);
// determine if adding a send from sendFrom to
// sendTo would result in an infinite mixer loop.
bool isInfiniteLoop(fx_ch_t fromChannel, fx_ch_t toChannel);
// return the FloatModel of fromChannel sending its output to the input of
// toChannel. NULL if there is no send.
FloatModel * channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel);
// add a new channel to the Fx Mixer.
// returns the index of the channel that was just added
int createChannel();
// delete a channel from the FX mixer.
void deleteChannel(int index);
// delete all the mixer channels except master and remove all effects
void clear();
// re-arrange channels
void moveChannelLeft(int index);
void moveChannelRight(int index);
// reset a channel's name, fx, sends, etc
void clearChannel(fx_ch_t channelIndex);
inline fx_ch_t numChannels() const
{
return m_fxChannels.size();
}
private:
FxChannel * m_fxChannels[NumFxChannels+1]; // +1 = master
// the fx channels in the mixer. index 0 is always master.
QVector<FxChannel *> m_fxChannels;
// make sure we have at least num channels
void allocateChannelsTo(int num);
void addChannelLeaf( int _ch, sampleFrame * _buf );
friend class mixerWorkerThread;
friend class FxMixerView;

View File

@@ -26,59 +26,91 @@
#define _FX_MIXER_VIEW_H
#include <QtGui/QWidget>
#include <QtGui/QHBoxLayout>
#include <QtGui/QScrollArea>
#include "FxLine.h"
#include "FxMixer.h"
#include "ModelView.h"
#include "engine.h"
#include "fader.h"
#include "pixmap_button.h"
#include "tooltip.h"
#include "embed.h"
#include "EffectRackView.h"
class QStackedLayout;
class QButtonGroup;
class fader;
class FxLine;
class EffectRackView;
class pixmapButton;
class FxMixerView : public QWidget, public ModelView,
public SerializingObjectHook
{
Q_OBJECT
public:
struct FxChannelView
{
FxChannelView(QWidget * _parent, FxMixerView * _mv, int _chIndex );
FxLine * m_fxLine;
pixmapButton * m_muteBtn;
fader * m_fader;
};
FxMixerView();
virtual ~FxMixerView();
virtual void keyPressEvent(QKeyEvent * e);
virtual void saveSettings( QDomDocument & _doc, QDomElement & _this );
virtual void loadSettings( const QDomElement & _this );
FxLine * currentFxLine()
inline FxLine * currentFxLine()
{
return m_currentFxLine;
}
inline FxChannelView * channelView(int index)
{
return m_fxChannelViews[index];
}
void setCurrentFxLine( FxLine * _line );
void setCurrentFxLine( int _line );
void clear();
// display the send button and knob correctly
void updateFxLine(int index);
// notify the view that an fx channel was deleted
void deleteChannel(int index);
// move the channel to the left or right
void moveChannelLeft(int index);
void moveChannelRight(int index);
// make sure the display syncs up with the fx mixer.
// useful for loading projects
void refreshDisplay();
private slots:
void updateFaders();
void addNewChannel();
private:
struct FxChannelView
{
FxLine * m_fxLine;
EffectRackView * m_rackView;
pixmapButton * m_muteBtn;
fader * m_fader;
} ;
FxChannelView m_fxChannelViews[NumFxChannels+1];
QVector<FxChannelView *> m_fxChannelViews;
QStackedLayout * m_fxRacksLayout;
QStackedLayout * m_fxLineBanks;
QButtonGroup * m_bankButtons;
FxLine * m_currentFxLine;
QScrollArea * channelArea;
QHBoxLayout * chLayout;
QWidget * m_channelAreaWidget;
EffectRackView * m_rackView;
void updateMaxChannelSelector();
} ;
#endif

View File

@@ -29,7 +29,7 @@
#include <QtGui/QWidget>
#include "Plugin.h"
#include "mixer.h"
#include "Mixer.h"
// forward-declarations

View File

@@ -25,7 +25,7 @@
#ifndef _INSTRUMENT_SOUND_SHAPING_H
#define _INSTRUMENT_SOUND_SHAPING_H
#include "mixer.h"
#include "Mixer.h"
#include "ComboBoxModel.h"

View File

@@ -26,6 +26,7 @@
#define _MAIN_WINDOW_H
#include <QtCore/QBasicTimer>
#include <QtCore/QTimer>
#include <QtCore/QList>
#include <QtGui/QMainWindow>
#include <QtGui/QWhatsThis>
@@ -125,6 +126,7 @@ public slots:
bool saveProject();
bool saveProjectAs();
void showSettingsDialog();
void showPreferencesDialog();
void aboutLMMS();
void help();
void toggleAutomationEditorWin();
@@ -207,6 +209,7 @@ private:
QList<PluginView *> m_tools;
QBasicTimer m_updateTimer;
QTimer m_autoSaveTimer;
ResourceBrowser * m_resourceBrowser;
@@ -243,6 +246,8 @@ private slots:
void playAndRecord();
void stop();
void autoSave();
signals:
void periodicUpdate();

View File

@@ -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 <tobydox/at/users.sourceforge.net>
*
@@ -39,19 +39,18 @@
#include <QtCore/QMutex>
#include <QtCore/QThread>
#include <QtCore/QVector>
#include <QtCore/QWaitCondition>
#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;
@@ -62,110 +61,17 @@ const Keys BaseKey = Key_A;
const Octaves BaseOctave = DefaultOctave;
class MixerWorkerThread;
#include "ThreadableJob.h"
#include "play_handle.h"
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 +82,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 +156,7 @@ public:
return m_framesPerPeriod;
}
inline const surroundSampleFrame * currentReadBuffer() const
inline const sampleFrameA * currentReadBuffer() const
{
return m_readBuf;
}
@@ -257,11 +167,6 @@ public:
return m_cpuLoad;
}
const qualitySettings & currentQualitySettings() const
{
return m_qualitySettings;
}
sample_rate_t baseSampleRate() const;
sample_rate_t outputSampleRate() const;
@@ -325,11 +230,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 +237,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 +249,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<surroundSampleFrame *> 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<AudioPort *> m_audioPorts;
fpp_t m_framesPerPeriod;
@@ -415,21 +281,21 @@ private:
f_cnt_t m_inputBufferSize[2];
int m_inputBufferRead;
int m_inputBufferWrite;
surroundSampleFrame * m_readBuf;
surroundSampleFrame * m_writeBuf;
QVector<surroundSampleFrame *> m_bufferPool;
sampleFrameA * m_readBuf;
sampleFrameA * m_writeBuf;
QVector<sampleFrameA *> 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<MixerWorkerThread *> m_workers;
int m_numWorkers;
@@ -439,12 +305,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,14 +321,8 @@ private:
QMutex m_inputFramesMutex;
fifo * m_fifo;
fifoWriter * m_fifoWriter;
friend class engine;
friend class MixerWorkerThread;
} ;
#endif

117
include/MixerWorkerThread.h Normal file
View File

@@ -0,0 +1,117 @@
/*
* MixerWorkerThread.h - declaration of class MixerWorkerThread
*
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 _MIXER_WORKER_THREAD_H
#define _MIXER_WORKER_THREAD_H
#include <QtCore/QAtomicPointer>
#include <QtCore/QThread>
#include "Mixer.h"
class MixerWorkerThread : public QThread
{
public:
// internal representation of the job queue - all functions are thread-safe
class JobQueue
{
public:
enum OperationMode
{
Static, // no jobs added while processing queue
Dynamic // jobs can be added while processing queue
} ;
JobQueue() :
m_items(),
m_queueSize( 0 ),
m_itemsDone( 0 ),
m_opMode( Static )
{
}
void reset( OperationMode _opMode );
void addJob( ThreadableJob * _job );
void run( sampleFrame * _buffer );
void wait();
private:
#define JOB_QUEUE_SIZE 1024
QAtomicPointer<ThreadableJob> m_items[JOB_QUEUE_SIZE];
QAtomicInt m_queueSize;
QAtomicInt m_itemsDone;
OperationMode m_opMode;
} ;
MixerWorkerThread( Mixer * _mixer );
virtual ~MixerWorkerThread();
virtual void quit();
static void resetJobQueue( JobQueue::OperationMode _opMode =
JobQueue::Static )
{
globalJobQueue.reset( _opMode );
}
static void addJob( ThreadableJob * _job )
{
globalJobQueue.addJob( _job );
}
// a convenient helper function allowing to pass a container with pointers
// to ThreadableJob objects
template<typename T>
static void fillJobQueue( const T & _vec,
JobQueue::OperationMode _opMode = JobQueue::Static )
{
resetJobQueue( _opMode );
for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it )
{
addJob( *it );
}
}
static void startAndWaitForJobs();
private:
virtual void run();
static JobQueue globalJobQueue;
static QWaitCondition * queueReadyWaitCond;
static QList<MixerWorkerThread *> workerThreads;
sampleFrame * m_workingBuf;
volatile bool m_quit;
} ;
#endif

View File

@@ -0,0 +1,43 @@
/*
* PreferencesDialog.h - declaration of class PreferencesDialog
*
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 _PREFERENCES_DIALOG_H
#define _PREFERENCES_DIALOG_H
#include <QtGui/QDialog>
namespace Ui { class PreferencesDialog; }
class PreferencesDialog : public QDialog
{
public:
PreferencesDialog();
private:
Ui::PreferencesDialog * ui;
} ;
#endif

View File

@@ -26,42 +26,46 @@
#define _PROJECT_RENDERER_H
#include "AudioFileDevice.h"
#include "AudioOutputContext.h"
#include "lmmsconfig.h"
class QTimer;
/*! \brief The ProjectRenderer class provides functionality to render current Song into a file. */
class ProjectRenderer : public QThread
{
Q_OBJECT
public:
/*! Lists all supported output file formats. */
enum ExportFileFormats
{
WaveFile,
OggFile,
Mp3File,
FlacFile,
WaveFile, /*!< Uncompressed WAV file */
OggFile, /*!< Vorbis-encoded OGG file */
Mp3File, /*!< MP3 file encoded via LAME */
FlacFile, /*!< Free Lossless Audio Codec */
NumFileFormats
} ;
static const char * EFF_ext[];
/*! Lists all supported sample type depths. */
enum Depths
{
Depth_16Bit,
Depth_24Bit,
Depth_32Bit,
Depth_16Bit, /*!< 16 bit signed integer */
Depth_24Bit, /*!< 24 bit floating point */
Depth_32Bit, /*!< 32 bit floating point */
NumDepths
} ;
struct OutputSettings
/*! Settings for the output file encoder. */
struct EncoderSettings
{
sample_rate_t samplerate;
bool vbr;
int bitrate;
Depths depth;
sample_rate_t samplerate; /*!< Desired output sample rate */
bool vbr; /*!< Use variable bitrate encoding */
int bitrate; /*!< Desired bitrate (kbps) */
Depths depth; /*!< Depth of samples */
OutputSettings( sample_rate_t _sr, bool _vbr, int _bitrate,
Depths _d ) :
EncoderSettings( sample_rate_t _sr, bool _vbr, int _bitrate, Depths _d ) :
samplerate( _sr ),
vbr( _vbr ),
bitrate( _bitrate ),
@@ -70,30 +74,40 @@ public:
}
} ;
ProjectRenderer( const mixer::qualitySettings & _qs,
const OutputSettings & _os,
ExportFileFormats _file_format,
const QString & _out_file );
/*! \brief Constructs a ProjectRenderer object with given settings.
*
* \param qualitySettings The desired quality settings for the AudioOutputContext
* \param encoderSettings The desired settings for the output file encoder
* \param fileFormat One of the file formats listed in the ExportFileFormats enumeration
* \param outFile The output file name
*/
ProjectRenderer( const AudioOutputContext::QualitySettings & qualitySettings,
const EncoderSettings & encoderSettings,
ExportFileFormats fileFormat,
const QString & outFile );
virtual ~ProjectRenderer();
/*! \brief Returns whether the ProjectRenderer was initialized properly. */
bool isReady() const
{
return m_fileDev != NULL;
}
static ExportFileFormats getFileFormatFromExtension(
const QString & _ext );
static ExportFileFormats getFileFormatFromExtension( const QString & _ext );
void setConsoleUpdateTimer(QTimer * t)
void setConsoleUpdateTimer( QTimer * t )
{
m_consoleUpdateTimer = t;
}
public slots:
/*! \brief Sets according AudioOutputContext for Mixer and starts render thread. */
void startProcessing();
/*! \brief Aborts the processing and cleans up resources. */
void abortProcessing();
/*! \brief Prints current render progress to the console. */
void updateConsoleProgress();
@@ -101,12 +115,16 @@ signals:
void progressChanged( int );
private slots:
/*! \brief Finalizes the render process and restores Mixer's AudioOutputContext. */
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;
@@ -116,6 +134,7 @@ private:
} ;
/*! \brief Holds information about a certain file encoder. */
struct FileEncodeDevice
{
ProjectRenderer::ExportFileFormats m_fileFormat;

View File

@@ -30,36 +30,59 @@ class trackContainer;
class ResourceItem;
/*! \brief The ResourceAction class provides centralized functionality for all actions
* related to a ResourceItem.
*
* These actions are for example loading projects, samples, presets,
* plugin-specific presets etc. Using this class we can avoid duplicated
* functionality in ResourcePreviewer, ResourceBrowser, TrackContainerView,
* InstrumentTrack & Co.
*/
class ResourceAction
{
public:
/*! Lists all supported actions. */
enum Actions
{
EditProperties,
LoadProject,
LoadInNewTrackSongEditor,
LoadInNewTrackBBEditor,
LoadInActiveInstrumentTrack,
DownloadIntoCollection,
UploadToWWW,
DeleteLocalResource,
ImportFile,
EditProperties, /*!< Open a dialog to edit properties of the ResourceItem */
LoadProject, /*!< Load the project represented by the ResourceItem */
LoadInNewTrackSongEditor, /*!< Load preset, sample etc. in a new track in Song Editor */
LoadInNewTrackBBEditor, /*!< Load preset, sample etc. in a new track in BB Editor */
LoadInActiveInstrumentTrack,/*!< Load preset, sample etc. in active instrument track */
DownloadIntoCollection, /*!< Download the resource into local collection */
UploadToWWW, /*!< Upload the resource to Web */
DeleteLocalResource, /*!< Delete local resource (=file) */
ImportFile, /*!< Try to import the resource via import filter plugins */
NumActions
} ;
typedef Actions Action;
ResourceAction( const ResourceItem * _item,
Action _action = NumActions ) :
m_action( _action ),
m_item( _item )
/*! \brief Constructs a ResourceAction object.
* \param item The ResourceItem the action is about
* \param action An optional action from the Action enumeration used for the defaultTrigger() method
*/
ResourceAction( const ResourceItem * item,
Action action = NumActions ) :
m_action( action ),
m_item( item )
{
}
bool loadProject();
bool loadByPlugin( InstrumentTrack * _target );
bool loadPreset( InstrumentTrack * _target );
bool importProject( trackContainer * _target );
// most actions can be triggered without any further information
bool loadByPlugin( InstrumentTrack * target );
bool loadPreset( InstrumentTrack * target );
bool importProject( trackContainer * target );
/*! \brief Triggers the action passed to the constructor without any further options.
*
* Most actions can be triggered without any further information.
* This allows simple but powerful code constructs:
* \code
* ResourceAction( myItem, ResourceAction::LoadProject ).defaultTrigger();
* \endcode
* \return true if the operation succeeded, otherwise false.
*/
bool defaultTrigger();

View File

@@ -34,20 +34,45 @@
#include "ResourceItem.h"
/*! \brief The ResourceDB class organizes and caches ResourceItems.
*
* ResourceItem sets are organized in the ResourceDB::ItemHashMap. This
* allows fast lookup of ResourceItems by hash string. ResourceItems are added
* and removed by a ResourceProvider.
*
* The ResourceItem array can be cached to a file and restored later. This is
* essential as otherwise e.g. the LocalResourceProvider would have to re-read
* all directories and files and recompute hash strings based on file contents.
*
* One can also descend the hierarchical ResourceItem tree by starting from
* ResourceDB::topLevelNode().
*/
class EXPORT ResourceDB : public QObject
{
Q_OBJECT
public:
/*! A QHash instantiation used for storing ResourceItems and allow lookup
* by hash string */
typedef QHash<QString, ResourceItem *> ItemHashMap;
ResourceDB( ResourceProvider * _provider );
/*! \brief Constructs a ResourceDB object.
* \param provider The ResourceProvider that will manage ResourceItems in this DB
*/
ResourceDB( ResourceProvider * provider );
~ResourceDB();
/*! \brief Initializes ResourceDB object, i.e. restore cache and update itself via the ResourceProvider. */
void init();
void load( const QString & _file );
void save( const QString & _file );
/*! \brief Dumps all ResourceItems with their relations to a file.
* \param file The filename to save data to */
void load( const QString & file );
/*! \brief Restores ResourceItems with their relations from a file.
* \param file The filename to load data from */
void save( const QString & file );
inline ResourceProvider * provider()
{
@@ -69,21 +94,31 @@ public:
return &m_topLevelNode;
}
// similiar to items()[_hash] but faster and returns NULL if not found
const ResourceItem * itemByHash( const QString & _hash ) const;
/*! \brief Looks up a ResourceItem by hash string - similiar to items()[hash]
* but faster and returns NULL if not found.
* \param hash The hash string that is searched for
* \return A const pointer to the matching ResourceItem */
const ResourceItem * itemByHash( const QString & hash ) const;
// return a list of ResourceItems who somehow match the given keywords
ResourceItemList matchItems( const QStringList & _keyWords );
/*! \brief Return a list of ResourceItems which somehow match the given keywords.
* \param keywords A list of keywords that are searched for
* \return A ResourceItemList which the result */
ResourceItemList matchItems( const QStringList & keywords );
// return an item which matches a resource desceibed in _item as
// good as possible
const ResourceItem * nearestMatch( const ResourceItem & _item );
/*! \brief Returns a ResourceItem which matches a given ResourceItem best.
* \param item A ResourceItem to search the best match for
* \return A const pointer to the best marching ResourceItem in the DB */
const ResourceItem * nearestMatch( const ResourceItem & item );
// add given item to DB
void addItem( ResourceItem * _newItem );
/*! \brief Adds given ResourceItem to DB.
* \param newItem The ResourceItem to be added */
void addItem( ResourceItem * newItem );
void recursiveRemoveItems( ResourceItem::Relation * parent,
bool removeTopLevelParent = true );
/*! \brief Remove items recursively starting from a certain ResourceItem::Relation
* \param parent The parent relation to start from with removing
* \param removeParent A boolean which specifies whether to also remove the parent item */
void removeItemsRecursively( ResourceItem::Relation * parent,
bool removeParent = true );
private:
@@ -140,9 +175,12 @@ private:
signals:
/*! \brief Forwarded signal of ResourceProvider::itemsChanged() */
void itemsChanged();
void directoryItemAdded( const QString & _path );
void directoryItemRemoved( const QString & _path );
/*! \brief Emitted whenever a ResourceItem of type ResourceItem::TypeDirectory is added */
void directoryItemAdded( const QString & path );
/*! \brief Emitted whenever a ResourceItem of type ResourceItem::TypeDirectory is removed */
void directoryItemRemoved( const QString & path );
} ;

View File

@@ -35,59 +35,96 @@
#include "TreeRelation.h"
/*! \brief The ResourceItem class provides information about a local/remote file/directory.
*
* All relevant properties of a file or directory are stored within a
* ResourceItem and can be accessed easily. All resources are identified by
* a unique hash (based on file content or (absolute) directory name).
*
* ResourceItems are managed within a ResourceDB. Reading and writing resource
* data is abstracted into the ResourceProvider class.
*
* The ResourceItem class does not provide any actual functionality.
* Use ResourceAction or more high level classes like ResourcePreviewer and
* ResourceBrowser.
*/
class EXPORT ResourceItem
{
public:
/*! A relation specifies how ResourceItems are organized among each other.
* See documentation of TreeRelation for details. */
typedef TreeRelation<ResourceItem> Relation;
/*! Lists all supported base directories for ResourceItems. */
enum BaseDirectories
{
BaseRoot,
BaseWorkingDir,
BaseDataDir,
BaseHome,
BaseURL,
BaseRoot, /*!< Item is relative to root directory */
BaseWorkingDir, /*!< Item is relative to working directory */
BaseDataDir, /*!< Item is relative to LMMS' data directory */
BaseHome, /*!< Item is relative to user's home directory */
BaseURL, /*!< Item is relative to the URL of the ResourceProvider */
NumBaseDirectories
} ;
typedef BaseDirectories BaseDirectory;
/*! Lists all supported ResourceItem types. */
enum Types
{
TypeUnknown,
TypeDirectory,
TypeSample,
TypePreset,
TypePluginSpecificResource,
TypeProject,
TypeMidiFile,
TypeForeignProject,
TypePlugin,
TypeImage,
TypeUnknown, /*!< No known resource type */
TypeDirectory, /*!< Item is a directory */
TypeSample, /*!< Item is a supported sample file */
TypePreset, /*!< Item is a LMMS-specific preset */
TypePluginSpecificResource, /* Item is a file supported by one of the available plugins */
TypeProject, /*!< Item is a LMMS project */
TypeMidiFile, /*!< Item is a MIDI file (and can be imported via MIDI import filter) */
TypeForeignProject, /*!< Item is any other kind of project which can be imported via an ImportFilter plugin */
TypePlugin, /*!< Item is a Plugin binary */
TypeImage, /*!< Item is an image */
NumTypes
} ;
typedef Types Type;
ResourceItem( ResourceProvider * _provider,
const QString & _name,
/*! \brief Constructs a ResourceItem object.
* \param provider The provider this item belongs to
* \param name The name used to identify the item towards the user
* \param baseDir The base directory this item is relative to
* \param path The path from base directory to this item
* \param hash A unique hash based on file content, pass QString::null to compute it automatically
* \param author A string describing the author
* \param tags A comma-separated list of tags for this item
* \param size The size of the item, pass -1 to compute it automatically
* \param lastMod The date and time of the last modification of the item
*/
ResourceItem( ResourceProvider * provider,
const QString & name,
Type _type,
BaseDirectory _base_dir = BaseWorkingDir,
const QString & _path = QString::null,
const QString & _hash = QString::null,
const QString & _author = QString::null,
const QString & _tags = QString::null,
int _size = -1,
const QDateTime & _last_mod = QDateTime() );
// copy constructor
ResourceItem( const ResourceItem & _item );
BaseDirectory baseDir = BaseWorkingDir,
const QString & path = QString::null,
const QString & hash = QString::null,
const QString & author = QString::null,
const QString & tags = QString::null,
int size = -1,
const QDateTime & lastMod = QDateTime() );
/*! \brief Copy constructor. */
ResourceItem( const ResourceItem & item );
inline void setHidden( bool _h, const QAbstractItemModel * _model )
/*! \brief Sets hidden property for the given item model
* \param hidden A boolean specifying the desired value
* \param model A pointer to a QAbstractItemModel (allows to use this item
* for multiple models and views) */
inline void setHidden( bool hidden, const QAbstractItemModel * model )
{
m_hidden[_model] = _h;
m_hidden[model] = hidden;
}
inline bool isHidden( const QAbstractItemModel * _model ) const
/*! \brief Returns whether item is hidden for the given item model
* \param model A pointer to a QAbstractItemModel (allows to use this item
* for multiple models and views)
* \return true if the item is hidden, false otherwise */
inline bool isHidden( const QAbstractItemModel * model ) const
{
return m_hidden[_model];
return m_hidden[model];
}
@@ -206,6 +243,8 @@ public:
return m_provider->dataSize( this );
}
/*! \brief Fetch data (contents) of the resource via ResourceProvider.
* \return QByteArray with complete contents of the resource */
QByteArray fetchData( int _maxSize = -1 ) const
{
return m_provider->fetchData( this );
@@ -213,15 +252,19 @@ public:
void reload();
// returns true if all given keywords match name, tags etc.
/*! \brief Returns, whether keywords match certain properties.
* \return true if all given keywords match name, tags etc. */
bool keywordMatch( const QStringList & _keywords ) const;
// return true, if given ResourceItem is equal
/*! \brief Tests for equality with another ResourceItem.
* \return true, if given ResourceItem is equal */
bool operator==( const ResourceItem & _other ) const;
// rates equality with given item
/*! \brief Rates equality with another ResourceItem.
* \return An integer specifying how close the two ResourceItems are (between 0 and about 250) */
int equalityLevel( const ResourceItem & _other ) const;
/*! \brief Guesses resource type by various criteria */
Type guessType() const;
static const char * mimeKey()

View File

@@ -0,0 +1,32 @@
#ifndef SENDBUTTONINDICATOR_H
#define SENDBUTTONINDICATOR_H
#include <QDebug>
#include <QtGui/QLabel>
#include <QtGui/QPixmap>
#include "FxLine.h"
#include "FxMixerView.h"
class FxLine;
class FxMixerView;
class SendButtonIndicator : public QLabel {
public:
SendButtonIndicator( QWidget * _parent, FxLine * _owner,
FxMixerView * _mv);
virtual void mousePressEvent( QMouseEvent * e );
void updateLightStatus();
private:
FxLine * m_parent;
FxMixerView * m_mv;
QPixmap qpmOn;
QPixmap qpmOff;
FloatModel * getSendModel();
};
#endif // SENDBUTTONINDICATOR_H

84
include/ThreadableJob.h Normal file
View File

@@ -0,0 +1,84 @@
/*
* ThreadableJob.h - declaration of class ThreadableJob
*
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 _THREADABLE_JOB_H
#define _THREADABLE_JOB_H
#include <QtCore/QAtomicInt>
#include "Mixer.h"
class ThreadableJob
{
public:
enum ProcessingState
{
Unstarted,
Queued,
InProgress,
Done
};
ThreadableJob() :
m_state( ThreadableJob::Unstarted )
{
}
inline ProcessingState state() const
{
return static_cast<ProcessingState>( (int) m_state );
}
inline void reset()
{
m_state = Unstarted;
}
inline void queue()
{
m_state = Queued;
}
void process( sampleFrame * _working_buffer )
{
if( m_state.testAndSetOrdered( Queued, InProgress ) )
{
doProcessing( _working_buffer );
m_state = Done;
}
}
virtual bool requiresProcessing() const = 0;
protected:
virtual void doProcessing( sampleFrame * _working_buffer ) = 0;
QAtomicInt m_state;
} ;
#endif

View File

@@ -36,7 +36,7 @@
#include <math.h>
#include "lmms_basics.h"
#include "mixer.h"
#include "Mixer.h"
#include "templates.h"
#include "lmms_constants.h"

View File

@@ -59,7 +59,7 @@ public:
// LMMS Stuff
virtual void drawFxLine(QPainter * _painter, const QWidget *_fxLine,
const QString & _name, bool _active);
const QString & _name, bool _active, bool _sendToThis);
virtual void drawTrackContentBackground(QPainter * _painter,
const QSize & _size, const int _pixelsPerTact);

View File

@@ -51,7 +51,7 @@ protected slots:
private:
Uint8 m_currentLoad;
int m_currentLoad;
QPixmap m_temp;
QPixmap m_background;

View File

@@ -66,7 +66,7 @@ public:
virtual void unpolish( QWidget * widget );
virtual void drawFxLine( QPainter * _painter, const QWidget *_fxLine,
const QString & _name, bool _active );
const QString & _name, bool _active, bool _sendToThis );
virtual void drawTrackContentBackground( QPainter * _painter,
const QSize & _size, const int _pixelsPerTact );

View File

@@ -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;

View File

@@ -1,89 +0,0 @@
/*
* fifo_buffer.h - FIFO fixed-size buffer
*
* Copyright (c) 2007 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
* Copyright (c) 2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 <QtCore/QSemaphore>
template<typename T>
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

View File

@@ -91,7 +91,7 @@ public:
virtual void drawFxLine(QPainter * _painter, const QWidget *_fxLine,
const QString & _name, bool _active) = 0;
const QString & _name, bool _active, bool _sendToThis) = 0;
virtual void drawTrackContentBackground(QPainter * _painter,
const QSize & _size, const int _pixelsPerTact) = 0;

View File

@@ -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"

View File

@@ -31,6 +31,6 @@
#include <limits>
#include "mixer.h"
#include "Mixer.h"
#endif

View File

@@ -26,14 +26,14 @@
#define _PLAY_HANDLE_H
#include <QtCore/QThread>
#include <QtCore/QVector>
#include "lmms_basics.h"
#include "ThreadableJob.h"
#include "Mixer.h"
class track;
class playHandle
class playHandle : public ThreadableJob
{
public:
enum Types
@@ -71,6 +71,17 @@ public:
return m_type;
}
// required for ThreadableJob
virtual void doProcessing( sampleFrame * _working_buffer )
{
play( _working_buffer );
}
virtual bool requiresProcessing() const
{
return !done();
}
virtual void play( sampleFrame * _working_buffer ) = 0;
virtual bool done() const = 0;

View File

@@ -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"

View File

@@ -30,7 +30,7 @@
#include <QtCore/QPair>
#include <qobject.h>
#include "mixer.h"
#include "Mixer.h"
#include "sample_buffer.h"
class bbTrack;

View File

@@ -29,7 +29,7 @@
#include <QtCore/QMap>
#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<QString, AudioDevice::setupWidget *> AswMap;
typedef QMap<QString, AudioBackend::setupWidget *> AswMap;
typedef QMap<QString, MidiClient::setupWidget *> MswMap;
typedef QMap<QString, QString> trMap;

View File

@@ -151,9 +151,10 @@ public:
// file management
void createNewProject();
void createNewProjectFromTemplate( const QString & _template );
void loadProject( const QString & _file_name );
bool saveProject();
bool saveProjectAs( const QString & _file_name );
void loadProject( const QString & _filename );
bool guiSaveProject();
bool guiSaveProjectAs( const QString & _filename );
bool saveProjectFile( const QString & _filename );
inline const QString & projectFileName() const
{
return m_fileName;

View File

@@ -31,7 +31,7 @@
#include <QtGui/QWidget>
#include "AutomatableModel.h"
#include "mixer.h"
#include "Mixer.h"
class QPixmap;

View File

@@ -29,7 +29,7 @@
#include <QtGui/QWidget>
#include <QtGui/QPixmap>
#include "mixer.h"
#include "Mixer.h"
class visualizationWidget : public QWidget

View File

@@ -27,6 +27,7 @@
#include <QtGui/QProgressDialog>
#include <QtCore/QDir>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>
#include "FlpImport.h"
#include "note_play_handle.h"
@@ -40,6 +41,7 @@
#include "Effect.h"
#include "engine.h"
#include "FxMixer.h"
#include "FxMixerView.h"
#include "group_box.h"
#include "Instrument.h"
#include "InstrumentTrack.h"
@@ -104,7 +106,7 @@ extern QString outstring;
}
const int NumFLFxChannels = 64;
static void dump_mem( const void * buffer, uint n_bytes )
{
@@ -542,7 +544,7 @@ struct FL_Project
int currentPattern;
int activeEditPattern;
FL_EffectChannel effectChannels[NumFxChannels+1];
FL_EffectChannel effectChannels[NumFLFxChannels+1];
int currentEffectChannel;
QString projectNotes;
@@ -903,7 +905,6 @@ bool FlpImport::tryFLPImport( trackContainer * _tc )
const bool is_journ = engine::projectJournal()->isJournalling();
engine::projectJournal()->setJournalling( false );
while( file().atEnd() == false )
{
FLP_Events ev = static_cast<FLP_Events>( readByte() );
@@ -1022,7 +1023,7 @@ bool FlpImport::tryFLPImport( trackContainer * _tc )
break;
case FLP_EffectChannelMuted:
if( p.currentEffectChannel <= NumFxChannels )
if( p.currentEffectChannel <= NumFLFxChannels )
{
p.effectChannels[p.currentEffectChannel].isMuted =
( data & 0x08 ) > 0 ? false : true;
@@ -1274,7 +1275,7 @@ if( p.currentEffectChannel <= NumFxChannels )
case FLP_Text_EffectChanName:
++p.currentEffectChannel;
if( p.currentEffectChannel <= NumFxChannels )
if( p.currentEffectChannel <= NumFLFxChannels )
{
p.effectChannels[p.currentEffectChannel].name = text;
}
@@ -1497,7 +1498,7 @@ if( p.currentEffectChannel <= NumFxChannels )
const int param = pi[i*3+1] & 0xffff;
const int ch = ( pi[i*3+1] >> 22 )
& 0x7f;
if( ch < 0 || ch > NumFxChannels )
if( ch < 0 || ch > NumFLFxChannels )
{
continue;
}
@@ -1558,9 +1559,15 @@ else
// now create a project from FL_Project data structure
engine::getSong()->clearProject();
// configure the mixer
for( int i=0; i<NumFLFxChannels; ++i )
{
engine::fxMixer()->createChannel();
}
engine::fxMixerView()->refreshDisplay();
// set global parameters
engine::getSong()->setMasterVolume( p.mainVolume );
engine::getSong()->setMasterPitch( p.mainPitch );
@@ -1797,7 +1804,7 @@ p->putValue( jt->pos, value, false );
}
}
for( int fx_ch = 0; fx_ch <= NumFxChannels ; ++fx_ch )
for( int fx_ch = 0; fx_ch <= NumFLFxChannels ; ++fx_ch )
{
FxChannel * ch = engine::fxMixer()->effectChannel( fx_ch );
if( !ch )
@@ -1857,7 +1864,7 @@ p->putValue( jt->pos, value, false );
break;
}
if( effName.isEmpty() || it->fxChannel < 0 ||
it->fxChannel > NumFxChannels )
it->fxChannel > NumFLFxChannels )
{
continue;
}

View File

@@ -2,6 +2,7 @@
* ladspa_description.cpp - LADSPA plugin description
*
* Copyright (c) 2007 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -30,10 +31,11 @@
#include <QtGui/QScrollArea>
#include <QtGui/QVBoxLayout>
#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 );

View File

@@ -2,7 +2,8 @@
* ladspa_port_dialog.cpp - dialog to test a LADSPA plugin
*
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
*
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 <QtGui/QLayout>
@@ -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();
}
}

View File

@@ -25,14 +25,15 @@
#include <QtGui/QMessageBox>
#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();
}

View File

@@ -28,12 +28,13 @@
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#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 ) );
}

View File

@@ -37,7 +37,6 @@
#include "InstrumentView.h"
#include "led_checkbox.h"
#include "knob.h"
#include "mixer.h"
class lb302SynthView;
class notePlayHandle;

View File

@@ -33,9 +33,10 @@
#include <QtGui/QVBoxLayout>
#include "AudioDevice.h"
#include "AudioBackend.h"
#include "AudioOutputContext.h"
#include "Mixer.h"
#include "engine.h"
#include "mixer.h"
@@ -81,7 +82,7 @@ lv2Description::lv2Description( QWidget * _parent,
if( description->type == _type &&
(
_type != VALID ||
description->inputChannels <= engine::getMixer()->audioDev()->channels()
description->inputChannels <= engine::mixer()->audioOutputContext()->audioBackend()->channels()
)
)
{

View File

@@ -30,7 +30,7 @@
#include "embed.h"
#include "engine.h"
#include "mixer.h"
#include "Mixer.h"
lv2PortDialog::lv2PortDialog( const lv2_key_t & _key )

View File

@@ -30,6 +30,8 @@
#include <QtGui/QFileDialog>
#include <QtXml/QDomDocument>
#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<int>( 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-"

View File

@@ -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 )

View File

@@ -29,7 +29,6 @@
#include <QtCore/QMutex>
#include <QtGui/QWidget>
#include "mixer.h"
#include "JournallingObject.h"
#include "communication.h"

View File

@@ -44,11 +44,11 @@ AutomatableModel::AutomatableModel( DataType _type,
const QString & _display_name,
bool _default_constructed ) :
Model( _parent, _display_name, _default_constructed ),
m_minValue( _min ),
m_maxValue( _max ),
m_dataType( _type ),
m_value( _val ),
m_initValue( _val ),
m_minValue( _min ),
m_maxValue( _max ),
m_step( _step ),
m_range( _max - _min ),
m_journalEntryReady( false ),

View File

@@ -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"

View File

@@ -30,7 +30,6 @@
#include "song.h"
#include "engine.h"
#include "mixer.h"
#include "ControllerConnection.h"

View File

@@ -179,6 +179,25 @@ void bufMixNoOpt( sampleFrameA * RP _dst, const sampleFrameA * RP _src,
void bufMixCoeffNoOpt( sampleFrameA * RP _dst, const sampleFrameA * RP _src,
float _coeff, int _frames )
{
for( int i = 0; i < _frames; )
{
_dst[i+0][0] += _src[i+0][0]*_coeff;
_dst[i+0][1] += _src[i+0][1]*_coeff;
_dst[i+1][0] += _src[i+1][0]*_coeff;
_dst[i+1][1] += _src[i+1][1]*_coeff;
_dst[i+2][0] += _src[i+2][0]*_coeff;
_dst[i+2][1] += _src[i+2][1]*_coeff;
_dst[i+3][0] += _src[i+3][0]*_coeff;
_dst[i+3][1] += _src[i+3][1]*_coeff;
i += 4;
}
}
void bufMixLRCoeffNoOpt( sampleFrameA * RP _dst,
const sampleFrameA * RP _src,
float _left, float _right, int _frames )
@@ -306,6 +325,7 @@ MemCpyFunc memCpy = memCpyNoOpt;
MemClearFunc memClear = memClearNoOpt;
BufApplyGainFunc bufApplyGain = bufApplyGainNoOpt;
BufMixFunc bufMix = bufMixNoOpt;
BufMixCoeffFunc bufMixCoeff = bufMixCoeffNoOpt;
BufMixLRCoeffFunc bufMixLRCoeff = bufMixLRCoeffNoOpt;
UnalignedBufMixLRCoeffFunc unalignedBufMixLRCoeff = unalignedBufMixLRCoeffNoOpt;
BufWetDryMixFunc bufWetDryMix = bufWetDryMixNoOpt;
@@ -337,6 +357,7 @@ void memCpySSE( void * RP _dst, const void * RP _src, int _size );
void memClearSSE( void * RP _dst, int _size );
void bufApplyGainSSE( sampleFrameA * RP _dst, float _gain, int _frames );
void bufMixSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, int _frames );
void bufMixCoeffSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _coeff, int _frames );
void bufMixLRCoeffSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _left, float _right, int _frames );
void unalignedBufMixLRCoeffSSE( sampleFrame * RP _dst, const sampleFrame * RP _src, const float _left, const float _right, int _frames );
void bufWetDryMixSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _wet, float _dry, int _frames );
@@ -447,6 +468,7 @@ void init()
memClear = memClearSSE;
bufApplyGain = bufApplyGainSSE;
bufMix = bufMixSSE;
bufMixCoeff = bufMixCoeffSSE;
bufMixLRCoeff = bufMixLRCoeffSSE;
unalignedBufMixLRCoeff = unalignedBufMixLRCoeffSSE;
bufWetDryMix = bufWetDryMixSSE;

View File

@@ -229,6 +229,29 @@ void bufMixSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src,
}
void bufMixCoeffSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src,
float _coeff, int _frames )
{
int i;
PREFETCH_READ(_src);
PREFETCH_WRITE(_dst);
for( i = 0; i < _frames; )
{
_dst[i+0][0] += _src[i+0][0]*_coeff;
_dst[i+0][1] += _src[i+0][1]*_coeff;
_dst[i+1][0] += _src[i+1][0]*_coeff;
_dst[i+1][1] += _src[i+1][1]*_coeff;
_dst[i+2][0] += _src[i+2][0]*_coeff;
_dst[i+2][1] += _src[i+2][1]*_coeff;
_dst[i+3][0] += _src[i+3][0]*_coeff;
_dst[i+3][1] += _src[i+3][1]*_coeff;
i += 4;
}
}
void bufMixLRCoeffSSE( sampleFrameA * RP _dst,
const sampleFrameA * RP _src,
float _left, float _right, int _frames )

View File

@@ -23,11 +23,11 @@
*
*/
#include <QtXml/QDomElement>
#include <cstdio>
#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" );

View File

@@ -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"

View File

@@ -22,18 +22,20 @@
*
*/
#include <QtXml/QDomElement>
#include "FxMixer.h"
#include "MixerWorkerThread.h"
#include "Cpu.h"
#include "Effect.h"
#include "song.h"
#include "InstrumentTrack.h"
#include "bb_track_container.h"
FxChannel::FxChannel( Model * _parent ) :
m_fxChain( NULL ),
m_used( false ),
m_stillRunning( false ),
m_peakLeft( 0.0f ),
m_peakRight( 0.0f ),
@@ -41,7 +43,8 @@ FxChannel::FxChannel( Model * _parent ) :
m_muteModel( false, _parent ),
m_volumeModel( 1.0, 0.0, 2.0, 0.01, _parent ),
m_name(),
m_lock()
m_lock(),
m_queued( false )
{
engine::getMixer()->clearAudioBuffer( m_buffer,
engine::getMixer()->framesPerPeriod() );
@@ -57,34 +60,361 @@ FxChannel::~FxChannel()
void FxChannel::doProcessing( sampleFrame * _buf )
{
FxMixer * fxm = engine::fxMixer();
const fpp_t fpp = engine::getMixer()->framesPerPeriod();
// <tobydox> ignore the passed _buf
// <tobydox> always use m_buffer
// <tobydox> this is just an auxilliary buffer if doProcessing()
// needs one for processing while running
// <tobydox> particularly important for playHandles, so Instruments
// can operate on this buffer the whole time
// <tobydox> this improves cache hit rate
_buf = m_buffer;
if( ! m_muteModel.value() )
{
// do mixer sends. loop through the channels that send to this one
for( int i = 0; i < m_receives.size(); ++i)
{
fx_ch_t senderIndex = m_receives[i];
FxChannel * sender = fxm->effectChannel(senderIndex);
// mix it with this one
float amt = fxm->channelSendModel(senderIndex,
m_channelIndex)->value();
CPU::bufMixCoeff( _buf, sender->m_buffer,
sender->m_volumeModel.value() * amt, fpp );
}
const float v = m_volumeModel.value();
m_fxChain.startRunning();
m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp );
m_peakLeft = engine::getMixer()->peakValueLeft( _buf, fpp ) * v;
m_peakRight = engine::getMixer()->peakValueRight( _buf, fpp ) * v;
}
else
{
m_peakLeft = m_peakRight = 0.0f;
}
m_state = ThreadableJob::Done;
// check if any of its parents are now able to be processed
for(int i=0; i<m_sends.size(); ++i)
{
// if parent.unstarted and every parent.leaf.done:
FxChannel * parent = fxm->effectChannel(m_sends[i]);
if( parent->state() == ThreadableJob::Unstarted )
{
bool everyLeafDone = true;
for( int j=0; j<parent->m_receives.size(); ++j )
{
if( fxm->effectChannel( parent->m_receives[j] )->state() !=
ThreadableJob::Done )
{
everyLeafDone = false;
break;
}
}
if( everyLeafDone )
{
MixerWorkerThread::addJob(parent);
}
}
}
}
FxMixer::FxMixer() :
JournallingObject(),
Model( NULL )
Model( NULL ),
m_fxChannels()
{
for( int i = 0; i < NumFxChannels+1; ++i )
{
m_fxChannels[i] = new FxChannel( this );
}
// reset name etc.
clear();
// create master channel
createChannel();
}
FxMixer::~FxMixer()
{
for( int i = 0; i < NumFxChannels+1; ++i )
for( int i = 0; i < m_fxChannels.size(); ++i )
{
for( int j = 0; j < m_fxChannels[i]->m_sendAmount.size(); ++j)
{
delete m_fxChannels[i]->m_sendAmount[j];
}
delete m_fxChannels[i];
}
}
int FxMixer::createChannel()
{
// create new channel
m_fxChannels.push_back(new FxChannel( this ));
// reset channel state
int index = m_fxChannels.size() - 1;
clearChannel(index);
return index;
}
void FxMixer::deleteChannel(int index)
{
m_fxChannels[index]->m_lock.lock();
// go through every instrument and adjust for the channel index change
QVector<track *> songTrackList = engine::getSong()->tracks();
QVector<track *> bbTrackList = engine::getBBTrackContainer()->tracks();
QVector<track *> trackLists[] = {songTrackList, bbTrackList};
for(int tl=0; tl<2; ++tl)
{
QVector<track *> trackList = trackLists[tl];
for(int i=0; i<trackList.size(); ++i)
{
if( trackList[i]->type() == track::InstrumentTrack )
{
InstrumentTrack * inst = (InstrumentTrack *) trackList[i];
int val = inst->effectChannelModel()->value(0);
if( val == index )
{
// we are deleting this track's fx send
// send to master
inst->effectChannelModel()->setValue(0);
}
else if( val > index )
{
// subtract 1 to make up for the missing channel
inst->effectChannelModel()->setValue(val-1);
}
}
}
}
// delete all of this channel's sends and receives
for(int i=0; i<m_fxChannels[index]->m_sends.size(); ++i)
{
deleteChannelSend(index, m_fxChannels[index]->m_sends[i]);
}
for(int i=0; i<m_fxChannels[index]->m_receives.size(); ++i)
{
deleteChannelSend(m_fxChannels[index]->m_receives[i], index);
}
for(int i=0; i<m_fxChannels.size(); ++i)
{
// for every send/receive, adjust for the channel index change
for(int j=0; j<m_fxChannels[i]->m_sends.size(); ++j)
{
if( m_fxChannels[i]->m_sends[j] > index )
{
// subtract 1 to make up for the missing channel
--m_fxChannels[i]->m_sends[j];
}
}
for(int j=0; j<m_fxChannels[i]->m_receives.size(); ++j)
{
if( m_fxChannels[i]->m_receives[j] > index )
{
// subtract 1 to make up for the missing channel
--m_fxChannels[i]->m_receives[j];
}
}
}
// actually delete the channel
delete m_fxChannels[index];
m_fxChannels.remove(index);
}
void FxMixer::moveChannelLeft(int index)
{
// can't move master or first channel
if( index <= 1 || index >= m_fxChannels.size() )
{
return;
}
// channels to swap
int a = index - 1, b = index;
// go through every instrument and adjust for the channel index change
QVector<track *> songTrackList = engine::getSong()->tracks();
QVector<track *> bbTrackList = engine::getBBTrackContainer()->tracks();
QVector<track *> trackLists[] = {songTrackList, bbTrackList};
for(int tl=0; tl<2; ++tl)
{
QVector<track *> trackList = trackLists[tl];
for(int i=0; i<trackList.size(); ++i)
{
if( trackList[i]->type() == track::InstrumentTrack )
{
InstrumentTrack * inst = (InstrumentTrack *) trackList[i];
int val = inst->effectChannelModel()->value(0);
if( val == a )
{
inst->effectChannelModel()->setValue(b);
}
else if( val == b )
{
inst->effectChannelModel()->setValue(a);
}
}
}
}
for(int i=0; i<m_fxChannels.size(); ++i)
{
// for every send/receive, adjust for the channel index change
for(int j=0; j<m_fxChannels[i]->m_sends.size(); ++j)
{
if( m_fxChannels[i]->m_sends[j] == a )
{
m_fxChannels[i]->m_sends[j] = b;
}
else if( m_fxChannels[i]->m_sends[j] == b )
{
m_fxChannels[i]->m_sends[j] = a;
}
}
for(int j=0; j<m_fxChannels[i]->m_receives.size(); ++j)
{
if( m_fxChannels[i]->m_receives[j] == a )
{
m_fxChannels[i]->m_receives[j] = b;
}
else if( m_fxChannels[i]->m_receives[j] == b )
{
m_fxChannels[i]->m_receives[j] = a;
}
}
}
// actually do the swap
FxChannel * tmpChannel = m_fxChannels[a];
m_fxChannels[a] = m_fxChannels[b];
m_fxChannels[b] = tmpChannel;
}
void FxMixer::moveChannelRight(int index)
{
moveChannelLeft(index+1);
}
void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel,
float amount)
{
// find the existing connection
FxChannel * from = m_fxChannels[fromChannel];
for(int i=0; i<from->m_sends.size(); ++i){
if( from->m_sends[i] == toChannel )
{
// simply adjust the amount
from->m_sendAmount[i]->setValue(amount);
return;
}
}
// connection does not exist. create a new one
// add to from's sends
from->m_sends.push_back(toChannel);
from->m_sendAmount.push_back(new FloatModel(amount, 0, 1, 0.001, NULL,
tr("Amount to send")));
// add to to's receives
m_fxChannels[toChannel]->m_receives.push_back(fromChannel);
}
// delete the connection made by createChannelSend
void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel)
{
// delete the send
FxChannel * from = m_fxChannels[fromChannel];
FxChannel * to = m_fxChannels[toChannel];
// find and delete the send entry
for(int i=0; i<from->m_sends.size(); ++i) {
if( from->m_sends[i] == toChannel )
{
// delete this index
delete from->m_sendAmount[i];
from->m_sendAmount.remove(i);
from->m_sends.remove(i);
break;
}
}
// find and delete the receive entry
for(int i=0; i<to->m_receives.size(); ++i)
{
if( to->m_receives[i] == fromChannel )
{
// delete this index
to->m_receives.remove(i);
break;
}
}
}
bool FxMixer::isInfiniteLoop(fx_ch_t sendFrom, fx_ch_t sendTo) {
// can't send master to anything
if( sendFrom == 0 ) return true;
// can't send channel to itself
if( sendFrom == sendTo ) return true;
// follow sendTo's outputs recursively looking for something that sends
// to sendFrom
for(int i=0; i<m_fxChannels[sendTo]->m_sends.size(); ++i)
{
if( isInfiniteLoop( sendFrom, m_fxChannels[sendTo]->m_sends[i] ) )
{
return true;
}
}
return false;
}
// how much does fromChannel send its output to the input of toChannel?
FloatModel * FxMixer::channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel)
{
FxChannel * from = m_fxChannels[fromChannel];
for(int i=0; i<from->m_sends.size(); ++i){
if( from->m_sends[i] == toChannel )
return from->m_sendAmount[i];
}
return NULL;
}
void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
{
@@ -93,7 +423,6 @@ void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
m_fxChannels[_ch]->m_lock.lock();
CPU::bufMix( m_fxChannels[_ch]->m_buffer, _buf,
engine::getMixer()->framesPerPeriod() );
m_fxChannels[_ch]->m_used = true;
m_fxChannels[_ch]->m_lock.unlock();
}
}
@@ -101,40 +430,6 @@ void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf )
{
if( m_fxChannels[_ch]->m_muteModel.value() == false &&
( m_fxChannels[_ch]->m_used ||
m_fxChannels[_ch]->m_stillRunning ||
_ch == 0 ) )
{
if( _buf == NULL )
{
_buf = m_fxChannels[_ch]->m_buffer;
}
const fpp_t f = engine::getMixer()->framesPerPeriod();
m_fxChannels[_ch]->m_fxChain.startRunning();
m_fxChannels[_ch]->m_stillRunning =
m_fxChannels[_ch]->m_fxChain.processAudioBuffer(
_buf, f );
m_fxChannels[_ch]->m_peakLeft =
engine::getMixer()->peakValueLeft( _buf, f ) *
m_fxChannels[_ch]->m_volumeModel.value();
m_fxChannels[_ch]->m_peakRight =
engine::getMixer()->peakValueRight( _buf, f ) *
m_fxChannels[_ch]->m_volumeModel.value();
m_fxChannels[_ch]->m_used = true;
}
else
{
m_fxChannels[_ch]->m_peakLeft =
m_fxChannels[_ch]->m_peakRight = 0.0f;
}
}
void FxMixer::prepareMasterMix()
{
engine::getMixer()->clearAudioBuffer( m_fxChannels[0]->m_buffer,
@@ -143,38 +438,53 @@ void FxMixer::prepareMasterMix()
void FxMixer::addChannelLeaf( int _ch, sampleFrame * _buf )
{
FxChannel * thisCh = m_fxChannels[_ch];
// remember what channel number we are, 'cause we need it later
thisCh->m_channelIndex = _ch;
// if we're muted or this channel is seen already, discount it
if( thisCh->m_muteModel.value() || thisCh->m_queued )
return;
int numDeps = thisCh->m_receives.size();
if( numDeps > 0 )
{
for(int i=0; i<numDeps; ++i)
{
addChannelLeaf( thisCh->m_receives[i], _buf );
}
}
else
{
// add this channel to job list
thisCh->m_queued = true;
MixerWorkerThread::addJob( thisCh );
}
}
void FxMixer::masterMix( sampleFrame * _buf )
{
const int fpp = engine::getMixer()->framesPerPeriod();
// recursively loop through channel dependency chain
// and add all channels to job list that have no dependencies
// when the channel completes it will check its parent to see if it needs
// to be processed.
MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
addChannelLeaf( 0, _buf );
while( m_fxChannels[0]->state() != ThreadableJob::Done )
{
MixerWorkerThread::startAndWaitForJobs();
}
memcpy( _buf, m_fxChannels[0]->m_buffer, sizeof( sampleFrame ) * fpp );
for( int i = 1; i < NumFxChannels+1; ++i )
{
if( m_fxChannels[i]->m_used )
{
sampleFrame * ch_buf = m_fxChannels[i]->m_buffer;
const float v = m_fxChannels[i]->m_volumeModel.value();
for( f_cnt_t f = 0; f < fpp; ++f )
{
_buf[f][0] += ch_buf[f][0] * v;
_buf[f][1] += ch_buf[f][1] * v;
}
engine::getMixer()->clearAudioBuffer( ch_buf,
engine::getMixer()->framesPerPeriod() );
m_fxChannels[i]->m_used = false;
}
}
processChannel( 0, _buf );
if( m_fxChannels[0]->m_muteModel.value() )
{
engine::getMixer()->clearAudioBuffer( _buf,
engine::getMixer()->framesPerPeriod() );
return;
}
const float v = m_fxChannels[0]->m_volumeModel.value();
for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f )
{
@@ -184,6 +494,16 @@ void FxMixer::masterMix( sampleFrame * _buf )
m_fxChannels[0]->m_peakLeft *= engine::getMixer()->masterGain();
m_fxChannels[0]->m_peakRight *= engine::getMixer()->masterGain();
// clear all channel buffers and
// reset channel process state
for( int i = 0; i < numChannels(); ++i)
{
engine::getMixer()->clearAudioBuffer( m_fxChannels[i]->m_buffer,
engine::getMixer()->framesPerPeriod() );
m_fxChannels[i]->reset();
m_fxChannels[i]->m_queued = false;
}
}
@@ -191,58 +511,138 @@ void FxMixer::masterMix( sampleFrame * _buf )
void FxMixer::clear()
{
for( int i = 0; i <= NumFxChannels; ++i )
while( m_fxChannels.size() > 1 )
{
m_fxChannels[i]->m_fxChain.clear();
m_fxChannels[i]->m_volumeModel.setValue( 1.0f );
m_fxChannels[i]->m_muteModel.setValue( false );
m_fxChannels[i]->m_name = ( i == 0 ) ?
tr( "Master" ) : tr( "FX %1" ).arg( i );
m_fxChannels[i]->m_volumeModel.setDisplayName(
m_fxChannels[i]->m_name );
deleteChannel(1);
}
clearChannel(0);
}
void FxMixer::clearChannel(fx_ch_t index)
{
FxChannel * ch = m_fxChannels[index];
ch->m_fxChain.clear();
ch->m_volumeModel.setValue( 1.0f );
ch->m_muteModel.setValue( false );
ch->m_name = ( index == 0 ) ? tr( "Master" ) : tr( "FX %1" ).arg( index );
ch->m_volumeModel.setDisplayName(ch->m_name );
// send only to master
if( index > 0)
{
// delete existing sends
for( int i=0; i<ch->m_sends.size(); ++i)
{
deleteChannelSend(index, ch->m_sends[i]);
}
// add send to master
createChannelSend(index, 0);
}
// delete receives
for( int i=0; i<ch->m_receives.size(); ++i)
{
deleteChannelSend(ch->m_receives[i], index);
}
}
void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
for( int i = 0; i <= NumFxChannels; ++i )
for( int i = 0; i < m_fxChannels.size(); ++i )
{
FxChannel * ch = m_fxChannels[i];
QDomElement fxch = _doc.createElement( QString( "fxchannel" ) );
_this.appendChild( fxch );
m_fxChannels[i]->m_fxChain.saveState( _doc, fxch );
m_fxChannels[i]->m_volumeModel.saveSettings( _doc, fxch,
"volume" );
m_fxChannels[i]->m_muteModel.saveSettings( _doc, fxch,
"muted" );
ch->m_fxChain.saveState( _doc, fxch );
ch->m_volumeModel.saveSettings( _doc, fxch, "volume" );
ch->m_muteModel.saveSettings( _doc, fxch, "muted" );
fxch.setAttribute( "num", i );
fxch.setAttribute( "name", m_fxChannels[i]->m_name );
fxch.setAttribute( "name", ch->m_name );
// add the channel sends
for( int si = 0; si < ch->m_sends.size(); ++si )
{
QDomElement sendsDom = _doc.createElement( QString( "send" ) );
fxch.appendChild( sendsDom );
sendsDom.setAttribute( "channel", ch->m_sends[si] );
ch->m_sendAmount[si]->saveSettings( _doc, sendsDom, "amount");
}
}
}
// make sure we have at least num channels
void FxMixer::allocateChannelsTo(int num)
{
while( num > m_fxChannels.size() - 1 )
{
createChannel();
// delete the default send to master
deleteChannelSend(m_fxChannels.size()-1, 0);
}
}
void FxMixer::loadSettings( const QDomElement & _this )
{
clear();
QDomNode node = _this.firstChild();
for( int i = 0; i <= NumFxChannels; ++i )
bool thereIsASend = false;
while( ! node.isNull() )
{
QDomElement fxch = node.toElement();
// index of the channel we are about to load
int num = fxch.attribute( "num" ).toInt();
m_fxChannels[num]->m_fxChain.restoreState(
fxch.firstChildElement(
m_fxChannels[num]->m_fxChain.nodeName() ) );
// allocate enough channels
allocateChannelsTo( num );
m_fxChannels[num]->m_volumeModel.loadSettings( fxch, "volume" );
m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" );
m_fxChannels[num]->m_name = fxch.attribute( "name" );
m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement(
m_fxChannels[num]->m_fxChain.nodeName() ) );
// mixer sends
QDomNodeList chData = fxch.childNodes();
for( unsigned int i=0; i<chData.length(); ++i )
{
QDomElement chDataItem = chData.at(i).toElement();
if( chDataItem.nodeName() == QString( "send" ) )
{
thereIsASend = true;
int sendTo = chDataItem.attribute( "channel" ).toInt();
allocateChannelsTo( sendTo) ;
float amount = chDataItem.attribute( "amount" ).toFloat();
createChannelSend( num, sendTo, amount );
}
}
node = node.nextSibling();
}
// check for old format. 65 fx channels and no explicit sends.
if( ! thereIsASend && m_fxChannels.size() == 65 ) {
// create a send from every channel into master
for( int i=1; i<m_fxChannels.size(); ++i )
{
createChannelSend(i, 0);
}
}
emit dataChanged();
}

View File

@@ -31,7 +31,6 @@
#include "song.h"
#include "engine.h"
#include "mixer.h"
#include "LfoController.h"
#include "ControllerDialog.h"

View File

@@ -278,7 +278,7 @@ qDebug() << "read dir" << d.canonicalPath();
{
ResourceItem::Relation * item = *it;
it = curParent->children().erase( it );
database()->recursiveRemoveItems( item );
database()->removeItemsRecursively( item );
}
else
{

View File

@@ -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 <tobydox/at/users.sourceforge.net>
*
@@ -24,8 +24,10 @@
#include <math.h>
#include "mixer.h"
#include "AudioOutputContext.h"
#include "Mixer.h"
#include "FxMixer.h"
#include "MixerWorkerThread.h"
#include "play_handle.h"
#include "song.h"
#include "templates.h"
@@ -61,215 +63,10 @@
#endif
static QVector<fx_ch_t> __fx_channel_jobs( NumFxChannels );
class MixerWorkerThread : public QThread
{
public:
enum JobTypes
{
InvalidJob,
PlayHandle,
AudioPortEffects,
EffectChannel,
NumJobTypes
} ;
struct JobQueueItem
{
JobQueueItem() :
type( InvalidJob ),
job( NULL ),
param( 0 ),
done( false )
{
}
JobQueueItem( JobTypes _type, void * _job, int _param = 0 ) :
type( _type ),
job( _job ),
param( _param ),
done( false )
{
}
JobTypes type;
void * job;
int param;
QAtomicInt done;
} ;
struct JobQueue
{
#define JOB_QUEUE_SIZE 1024
JobQueue() :
queueSize( 0 )
{
}
JobQueueItem items[JOB_QUEUE_SIZE];
int queueSize;
QAtomicInt itemsDone;
} ;
static JobQueue s_jobQueue;
MixerWorkerThread( int _worker_num, mixer * _mixer ) :
QThread( _mixer ),
m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ),
m_workerNum( _worker_num ),
m_quit( false ),
m_mixer( _mixer ),
m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond )
{
}
virtual ~MixerWorkerThread()
{
CPU::freeFrames( m_workingBuf );
}
virtual void quit()
{
m_quit = true;
}
void processJobQueue();
private:
virtual void run()
{
#if 0
#ifdef LMMS_BUILD_LINUX
#ifdef LMMS_HAVE_PTHREAD_H
cpu_set_t mask;
CPU_ZERO( &mask );
CPU_SET( m_workerNum, &mask );
pthread_setaffinity_np( pthread_self(), sizeof( mask ), &mask );
#endif
#endif
#endif
QMutex m;
while( m_quit == false )
{
m.lock();
m_queueReadyWaitCond->wait( &m );
processJobQueue();
m.unlock();
}
}
sampleFrame * m_workingBuf;
int m_workerNum;
volatile bool m_quit;
mixer * m_mixer;
QWaitCondition * m_queueReadyWaitCond;
} ;
MixerWorkerThread::JobQueue MixerWorkerThread::s_jobQueue;
void MixerWorkerThread::processJobQueue()
{
for( int i = 0; i < s_jobQueue.queueSize; ++i )
{
JobQueueItem * it = &s_jobQueue.items[i];
if( it->done.fetchAndStoreOrdered( 1 ) == 0 )
{
switch( it->type )
{
case PlayHandle:
( (playHandle *) it->job )->
play( m_workingBuf );
break;
case AudioPortEffects:
{
AudioPort * a = (AudioPort *) it->job;
const bool me = a->processEffects();
if( me || a->m_bufferUsage != AudioPort::NoUsage )
{
engine::fxMixer()->mixToChannel( a->firstBuffer(),
a->nextFxChannel() );
a->nextPeriod();
}
}
break;
case EffectChannel:
engine::fxMixer()->processChannel( (fx_ch_t) it->param );
break;
default:
break;
}
s_jobQueue.itemsDone.fetchAndAddOrdered( 1 );
}
}
}
#define FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \
MixerWorkerThread::s_jobQueue.queueSize = 0; \
MixerWorkerThread::s_jobQueue.itemsDone = 0; \
for( _vec_type::Iterator it = _vec.begin(); \
it != _vec.end(); ++it ) \
{ \
if( _condition ) \
{
#define FILL_JOB_QUEUE_END() \
++MixerWorkerThread::s_jobQueue.queueSize; \
} \
}
#define FILL_JOB_QUEUE(_vec_type,_vec,_job_type,_condition) \
FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \
MixerWorkerThread::s_jobQueue.items \
[MixerWorkerThread::s_jobQueue.queueSize] = \
MixerWorkerThread::JobQueueItem( _job_type, \
(void *) *it ); \
FILL_JOB_QUEUE_END()
#define FILL_JOB_QUEUE_PARAM(_vec_type,_vec,_job_type,_condition) \
FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \
MixerWorkerThread::s_jobQueue.items \
[MixerWorkerThread::s_jobQueue.queueSize] = \
MixerWorkerThread::JobQueueItem( _job_type, \
NULL, *it ); \
FILL_JOB_QUEUE_END()
#define START_JOBS() \
m_queueReadyWaitCond.wakeAll();
// define a pause instruction for spinlock-loop - merely useful on
// HyperThreading systems with just one physical core (e.g. Intel Atom)
#ifdef LMMS_HOST_X86
#define SPINLOCK_PAUSE() asm( "pause" )
#else
#ifdef LMMS_HOST_X86_64
#define SPINLOCK_PAUSE() asm( "pause" )
#else
#define SPINLOCK_PAUSE()
#endif
#endif
#define WAIT_FOR_JOBS() \
m_workers[m_numWorkers]->processJobQueue(); \
while( MixerWorkerThread::s_jobQueue.itemsDone < \
MixerWorkerThread::s_jobQueue.queueSize ) \
{ \
SPINLOCK_PAUSE(); \
} \
mixer::mixer() :
m_framesPerPeriod( DEFAULT_BUFFER_SIZE ),
Mixer::Mixer() :
m_framesPerPeriod( qBound<int>( 32,
configManager::inst()->value( "mixer", "framesperaudiobuffer" ).toInt(),
DEFAULT_BUFFER_SIZE ) ),
m_workingBuf( NULL ),
m_inputBufferRead( 0 ),
m_inputBufferWrite( 1 ),
@@ -279,58 +76,20 @@ 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] );
}
for( int i = 1; i < NumFxChannels+1; ++i )
{
__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++ )
{
@@ -341,7 +100,7 @@ mixer::mixer() :
for( int i = 0; i < m_numWorkers+1; ++i )
{
MixerWorkerThread * wt = new MixerWorkerThread( i, this );
MixerWorkerThread * wt = new MixerWorkerThread( this );
if( i < m_numWorkers )
{
wt->start( QThread::TimeCriticalPriority );
@@ -352,33 +111,29 @@ 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
MixerWorkerThread::s_jobQueue.queueSize = 0;
for( int w = 0; w < m_numWorkers; ++w )
{
m_workers[w]->quit();
}
START_JOBS();
MixerWorkerThread::startAndWaitForJobs();
for( int w = 0; w < m_numWorkers; ++w )
{
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 +147,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 +208,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 +245,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,23 +265,24 @@ 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;
song::playPos p = engine::getSong()->getPlayPos(
song::Mode_PlayPattern );
FxMixer * fxm = engine::fxMixer();
song::playPos p = engine::getSong()->getPlayPos( song::Mode_PlayPattern );
if( engine::getSong()->playMode() == song::Mode_PlayPattern &&
engine::getPianoRoll()->isRecording() == true &&
p != last_metro_pos && p.getTicks() %
@@ -575,18 +334,15 @@ sampleFrameA * mixer::renderNextBuffer()
clearAudioBuffer( m_writeBuf, m_framesPerPeriod );
// prepare master mix (clear internal buffers etc.)
engine::fxMixer()->prepareMasterMix();
fxm->prepareMasterMix();
// create play-handles for new notes, samples etc.
engine::getSong()->processNextBuffer();
// STAGE 1: run and render all play handles
FILL_JOB_QUEUE(PlayHandleList,m_playHandles,
MixerWorkerThread::PlayHandle,
!( *it )->done());
START_JOBS();
WAIT_FOR_JOBS();
MixerWorkerThread::fillJobQueue<PlayHandleList>( m_playHandles );
MixerWorkerThread::startAndWaitForJobs();
// removed all play handles which are done
for( PlayHandleList::Iterator it = m_playHandles.begin();
@@ -611,21 +367,11 @@ sampleFrameA * mixer::renderNextBuffer()
// STAGE 2: process effects of all instrument- and sampletracks
FILL_JOB_QUEUE(QVector<AudioPort*>,m_audioPorts,
MixerWorkerThread::AudioPortEffects,1);
START_JOBS();
WAIT_FOR_JOBS();
MixerWorkerThread::fillJobQueue<QVector<AudioPort *> >( m_audioPorts );
MixerWorkerThread::startAndWaitForJobs();
// STAGE 3: process effects in FX mixer
FILL_JOB_QUEUE_PARAM(QVector<fx_ch_t>,__fx_channel_jobs,
MixerWorkerThread::EffectChannel,1);
START_JOBS();
WAIT_FOR_JOBS();
// STAGE 4: do master mix in FX mixer
engine::fxMixer()->masterMix( m_writeBuf );
// STAGE 3: do master mix in FX mixer
fxm->masterMix( m_writeBuf );
unlock();
@@ -649,7 +395,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 +415,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 +455,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 +470,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 +491,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 +511,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<AudioPort *>::Iterator it = qFind( m_audioPorts.begin(),
m_audioPorts.end(),
@@ -881,7 +527,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 +554,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 +578,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 +592,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 +606,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 +620,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 +634,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 +648,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 +662,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 +673,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 +686,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 +757,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"

View File

@@ -0,0 +1,166 @@
/*
* MixerWorkerThread.cpp - implementation of MixerWorkerThread
*
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 "MixerWorkerThread.h"
#include "Cpu.h"
#include "engine.h"
MixerWorkerThread::JobQueue MixerWorkerThread::globalJobQueue;
QWaitCondition * MixerWorkerThread::queueReadyWaitCond = NULL;
QList<MixerWorkerThread *> MixerWorkerThread::workerThreads;
// implementation of internal JobQueue
void MixerWorkerThread::JobQueue::reset( OperationMode _opMode )
{
m_queueSize = 0;
m_itemsDone = 0;
m_opMode = _opMode;
}
void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job )
{
if( _job->requiresProcessing() )
{
// update job state
_job->queue();
// actually queue the job via atomic operations
m_items[m_queueSize.fetchAndAddOrdered(1)] = _job;
}
}
void MixerWorkerThread::JobQueue::run( sampleFrame * _buffer )
{
bool processedJob = true;
while( processedJob && (int) m_itemsDone < (int) m_queueSize )
{
processedJob = false;
for( int i = 0; i < m_queueSize; ++i )
{
ThreadableJob * job = m_items[i].fetchAndStoreOrdered( NULL );
if( job )
{
job->process( _buffer );
processedJob = true;
m_itemsDone.fetchAndAddOrdered( 1 );
}
}
// always exit loop if we're not in dynamic mode
processedJob = processedJob && ( m_opMode == Dynamic );
}
}
void MixerWorkerThread::JobQueue::wait()
{
while( (int) m_itemsDone < (int) m_queueSize )
{
#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64)
asm( "pause" );
#endif
}
}
// implementation of worker threads
MixerWorkerThread::MixerWorkerThread( Mixer * _mixer ) :
QThread( _mixer ),
m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ),
m_quit( false )
{
// initialize global static data
if( queueReadyWaitCond == NULL )
{
queueReadyWaitCond = new QWaitCondition;
}
// keep track of all instantiated worker threads - this is used for
// processing the last worker thread "inline", see comments in
// MixerWorkerThread::startAndWaitForJobs() for details
workerThreads << this;
resetJobQueue();
}
MixerWorkerThread::~MixerWorkerThread()
{
CPU::freeFrames( m_workingBuf );
workerThreads.removeAll( this );
}
void MixerWorkerThread::quit()
{
m_quit = true;
resetJobQueue();
}
void MixerWorkerThread::startAndWaitForJobs()
{
queueReadyWaitCond->wakeAll();
// The last worker-thread is never started. Instead it's processed "inline"
// i.e. within the global Mixer thread. This way we can reduce latencies
// that otherwise would be caused by synchronizing with another thread.
globalJobQueue.run( workerThreads.last()->m_workingBuf );
globalJobQueue.wait();
}
void MixerWorkerThread::run()
{
QMutex m;
while( m_quit == false )
{
m.lock();
queueReadyWaitCond->wait( &m );
globalJobQueue.run( m_workingBuf );
m.unlock();
}
}

View File

@@ -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 )
{

View File

@@ -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"

View File

@@ -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"

View File

@@ -22,7 +22,6 @@
*
*/
#include <QtCore/QFile>
#include <QTimer>
@@ -60,9 +59,9 @@ FileEncodeDevice __fileEncodeDevices[] =
".mp3", &AudioFileMp3::getInst },
{ ProjectRenderer::FlacFile,
QT_TRANSLATE_NOOP( "ProjectRenderer", "FLAC File (*.flac)" ),
".flac",
".flac",
#ifdef LMMS_HAVE_FLAC
&AudioFileFlac::getInst
&AudioFileFlac::getInst
#else
NULL
#endif
@@ -77,36 +76,40 @@ FileEncodeDevice __fileEncodeDevices[] =
const char * ProjectRenderer::EFF_ext[] = {"wav", "ogg", "mp3", "flac"};
ProjectRenderer::ProjectRenderer( const mixer::qualitySettings & _qs,
const OutputSettings & _os,
ExportFileFormats _file_format,
const QString & _out_file ) :
ProjectRenderer::ProjectRenderer(
const AudioOutputContext::QualitySettings & _qs,
const EncoderSettings & es,
ExportFileFormats fileFormat,
const QString & outFile ) :
QThread( engine::getMixer() ),
m_fileDev( NULL ),
m_qualitySettings( _qs ),
m_oldQualitySettings( engine::getMixer()->currentQualitySettings() ),
m_progress( 0 ),
m_abort( false )
{
if( __fileEncodeDevices[_file_format].m_getDevInst == NULL )
m_context = new AudioOutputContext( engine::getMixer(),
NULL,
_qs );
if( __fileEncodeDevices[fileFormat].m_getDevInst == NULL )
{
return;
}
bool success_ful = false;
m_fileDev = __fileEncodeDevices[_file_format].m_getDevInst(
_os.samplerate, DEFAULT_CHANNELS, success_ful,
_out_file, _os.vbr,
_os.bitrate, _os.bitrate - 64, _os.bitrate + 64,
_os.depth == Depth_32Bit ? 32 :
( _os.depth == Depth_24Bit ? 24 : 16 ),
engine::getMixer() );
m_fileDev = __fileEncodeDevices[fileFormat].m_getDevInst(
es.samplerate, DEFAULT_CHANNELS, success_ful,
outFile, es.vbr,
es.bitrate, es.bitrate - 64, es.bitrate + 64,
es.depth == Depth_32Bit ? 32 :
( es.depth == Depth_24Bit ? 24 : 16 ),
m_context );
if( success_ful == false )
{
delete m_fileDev;
m_fileDev = NULL;
}
m_context->setAudioBackend( m_fileDev );
}
@@ -114,6 +117,8 @@ ProjectRenderer::ProjectRenderer( const mixer::qualitySettings & _qs,
ProjectRenderer::~ProjectRenderer()
{
delete m_fileDev;
delete m_context;
}
@@ -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,66 +149,14 @@ 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
QThread::HighPriority
#endif
);
}
}
void ProjectRenderer::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
engine::getSong()->startExport();
song::playPos & pp = engine::getSong()->getPlayPos(
song::Mode_PlaySong );
m_progress = 0;
const int sl = ( engine::getSong()->length() + 1 ) * 192;
while( engine::getSong()->isExportDone() == false &&
engine::getSong()->isExporting() == true
&& !m_abort )
{
m_fileDev->processNextBuffer();
const int nprog = pp * 100 / sl;
if( m_progress != nprog )
{
m_progress = nprog;
emit progressChanged( m_progress );
}
}
engine::getSong()->stopExport();
const QString f = m_fileDev->outputFile();
engine::getMixer()->restoreAudioDevice(); // also deletes audio-dev
engine::getMixer()->changeQuality( m_oldQualitySettings );
// if the user aborted export-process, the file has to be deleted
if( m_abort )
{
QFile( f ).remove();
start();
}
}
@@ -217,6 +170,7 @@ void ProjectRenderer::abortProcessing()
void ProjectRenderer::updateConsoleProgress()
{
const int cols = 50;
@@ -224,7 +178,8 @@ void ProjectRenderer::updateConsoleProgress()
char buf[80];
char prog[cols+1];
if( m_fileDev == NULL ){
if( m_fileDev == NULL )
{
qWarning("Error occured. Aborting render.");
m_consoleUpdateTimer->stop();
delete m_consoleUpdateTimer;
@@ -250,5 +205,68 @@ void ProjectRenderer::updateConsoleProgress()
void ProjectRenderer::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
// have to lock Mixer when touching Song's state as the FIFO writer thread
// may call Mixer::renderNextBuffer() (which calls Song::doActions())
// simultaneously
engine::mixer()->lock();
engine::getSong()->startExport();
engine::mixer()->unlock();
song::playPos & pp = engine::getSong()->getPlayPos(
song::Mode_PlaySong );
m_progress = 0;
const int sl = ( engine::getSong()->length() + 1 ) * 192;
while( engine::getSong()->isExportDone() == false &&
engine::getSong()->isExporting() == true
&& !m_abort )
{
m_fileDev->processNextBuffer();
const int nprog = pp * 100 / sl;
if( m_progress != nprog )
{
m_progress = nprog;
emit progressChanged( m_progress );
}
}
engine::mixer()->lock();
engine::getSong()->stopExport();
engine::mixer()->unlock();
}
void ProjectRenderer::finishProcessing()
{
const QString f = m_fileDev->outputFile();
engine::mixer()->setAudioOutputContext(
engine::mixer()->defaultAudioOutputContext() );
// if the user aborted export-process, the file has to be deleted
if( m_abort )
{
QFile( f ).remove();
}
}
#include "moc_ProjectRenderer.cxx"

View File

@@ -29,7 +29,7 @@
#endif
#include "RemotePlugin.h"
#include "mixer.h"
#include "Mixer.h"
#include "engine.h"
#include "config_mgr.h"

View File

@@ -98,7 +98,7 @@ void ResourceDB::init()
void ResourceDB::load( const QString & _file )
{
recursiveRemoveItems( topLevelNode(), false );
removeItemsRecursively( topLevelNode(), false );
multimediaProject m( _file );
@@ -283,7 +283,7 @@ void ResourceDB::addItem( ResourceItem * newItem )
ResourceItem::Relation * oldRelation = oldItem->relation();
if( oldRelation )
{
recursiveRemoveItems( oldRelation, false );
removeItemsRecursively( oldRelation, false );
delete oldRelation;
}
if( oldItem->type() == ResourceItem::TypeDirectory )
@@ -299,8 +299,8 @@ void ResourceDB::addItem( ResourceItem * newItem )
void ResourceDB::recursiveRemoveItems( ResourceItem::Relation * parent,
bool removeTopLevelParent )
void ResourceDB::removeItemsRecursively( ResourceItem::Relation * parent,
bool removeParent )
{
if( !parent )
{
@@ -309,10 +309,10 @@ void ResourceDB::recursiveRemoveItems( ResourceItem::Relation * parent,
while( !parent->children().isEmpty() )
{
recursiveRemoveItems( parent->children().front() );
removeItemsRecursively( parent->children().front() );
}
if( removeTopLevelParent && parent->item() )
if( removeParent && parent->item() )
{
if( parent->item()->type() == ResourceItem::TypeDirectory )
{

View File

@@ -40,11 +40,11 @@
AudioAlsa::AudioAlsa( bool & _success_ful, mixer * _mixer ) :
AudioDevice( tLimit<ch_cnt_t>(
AudioAlsa::AudioAlsa( bool & _success_ful, AudioOutputContext * context ) :
AudioBackend( tLimit<ch_cnt_t>(
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 );

View File

@@ -0,0 +1,144 @@
/*
* AudioBackend.cpp - base-class for audio-devices used by LMMS-mixer
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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();
}

View File

@@ -1,208 +0,0 @@
/*
* AudioDevice.cpp - base-class for audio-devices used by LMMS-mixer
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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();
}

View File

@@ -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 ),

View File

@@ -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();
}

View File

@@ -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 ),

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -45,15 +45,15 @@
AudioJack::AudioJack( bool & _success_ful, mixer * _mixer ) :
AudioDevice( tLimit<int>( configManager::inst()->value(
AudioJack::AudioJack( bool & _success_ful, AudioOutputContext * context ) :
AudioBackend( tLimit<int>( 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<Uint32>( _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() )

Some files were not shown because too many files have changed in this diff Show More