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

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

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