Merge branch 'master' into calf-updates
BIN
data/themes/default/folder-64.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
data/themes/default/mixer_send_off.png
Normal file
|
After Width: | Height: | Size: 467 B |
BIN
data/themes/default/mixer_send_on.png
Normal file
|
After Width: | Height: | Size: 471 B |
BIN
data/themes/default/preferences-desktop-sound.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
data/themes/default/preferences-system.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
data/themes/default/send_bg_arrow.png
Normal file
|
After Width: | Height: | Size: 267 B |
BIN
data/themes/default/setup-midi.png
Executable file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
data/themes/default/setup-plugins.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 );
|
||||
|
||||
377
include/AudioOutputContext.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
} ;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
} ;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include "Plugin.h"
|
||||
#include "engine.h"
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
#include "AutomatableModel.h"
|
||||
#include "TempoSyncKnobModel.h"
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <QtGui/QWidget>
|
||||
|
||||
#include "Plugin.h"
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
// forward-declarations
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifndef _INSTRUMENT_SOUND_SHAPING_H
|
||||
#define _INSTRUMENT_SOUND_SHAPING_H
|
||||
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
#include "ComboBoxModel.h"
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
43
include/PreferencesDialog.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
32
include/SendButtonIndicator.h
Normal 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
@@ -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
|
||||
@@ -36,7 +36,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "lmms_basics.h"
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
#include "templates.h"
|
||||
#include "lmms_constants.h"
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -51,7 +51,7 @@ protected slots:
|
||||
|
||||
|
||||
private:
|
||||
Uint8 m_currentLoad;
|
||||
int m_currentLoad;
|
||||
|
||||
QPixmap m_temp;
|
||||
QPixmap m_background;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <QtCore/QPair>
|
||||
#include <qobject.h>
|
||||
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
#include "sample_buffer.h"
|
||||
|
||||
class bbTrack;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <QtGui/QWidget>
|
||||
|
||||
#include "AutomatableModel.h"
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
class QPixmap;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <QtGui/QWidget>
|
||||
#include <QtGui/QPixmap>
|
||||
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
class visualizationWidget : public QWidget
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "InstrumentView.h"
|
||||
#include "led_checkbox.h"
|
||||
#include "knob.h"
|
||||
#include "mixer.h"
|
||||
|
||||
class lb302SynthView;
|
||||
class notePlayHandle;
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
#include "embed.h"
|
||||
#include "engine.h"
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
lv2PortDialog::lv2PortDialog( const lv2_key_t & _key )
|
||||
|
||||
@@ -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-"
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtGui/QWidget>
|
||||
|
||||
#include "mixer.h"
|
||||
#include "JournallingObject.h"
|
||||
#include "communication.h"
|
||||
|
||||
|
||||
@@ -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 ),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
#include "song.h"
|
||||
#include "engine.h"
|
||||
#include "mixer.h"
|
||||
#include "ControllerConnection.h"
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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" );
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
#include "song.h"
|
||||
#include "engine.h"
|
||||
#include "mixer.h"
|
||||
#include "LfoController.h"
|
||||
#include "ControllerDialog.h"
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
166
src/core/MixerWorkerThread.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#endif
|
||||
|
||||
#include "RemotePlugin.h"
|
||||
#include "mixer.h"
|
||||
#include "Mixer.h"
|
||||
#include "engine.h"
|
||||
#include "config_mgr.h"
|
||||
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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 );
|
||||
|
||||
144
src/core/audio/AudioBackend.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 ),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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() )
|
||||
|
||||