@@ -22,55 +22,43 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AUDIO_PORT_H
|
||||
#define _AUDIO_PORT_H
|
||||
#ifndef AUDIO_PORT_H
|
||||
#define AUDIO_PORT_H
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QMutexLocker>
|
||||
|
||||
#include "Mixer.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "PlayHandle.h"
|
||||
|
||||
class EffectChain;
|
||||
class FloatModel;
|
||||
|
||||
class AudioPort : public ThreadableJob
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
AudioPort( const QString & _name, bool _has_effect_chain = true );
|
||||
AudioPort( const QString & _name, bool _has_effect_chain = true,
|
||||
FloatModel * volumeModel = NULL, FloatModel * panningModel = NULL );
|
||||
virtual ~AudioPort();
|
||||
|
||||
inline sampleFrame * firstBuffer()
|
||||
inline sampleFrame * buffer()
|
||||
{
|
||||
return m_firstBuffer;
|
||||
return m_portBuffer;
|
||||
}
|
||||
|
||||
inline sampleFrame * secondBuffer()
|
||||
inline void lockBuffer()
|
||||
{
|
||||
return m_secondBuffer;
|
||||
m_portBufferLock.lock();
|
||||
}
|
||||
|
||||
inline void lockFirstBuffer()
|
||||
inline void unlockBuffer()
|
||||
{
|
||||
m_firstBufferLock.lock();
|
||||
m_portBufferLock.unlock();
|
||||
}
|
||||
|
||||
inline void lockSecondBuffer()
|
||||
{
|
||||
m_secondBufferLock.lock();
|
||||
}
|
||||
|
||||
inline void unlockFirstBuffer()
|
||||
{
|
||||
m_firstBufferLock.unlock();
|
||||
}
|
||||
|
||||
inline void unlockSecondBuffer()
|
||||
{
|
||||
m_secondBufferLock.unlock();
|
||||
}
|
||||
|
||||
void nextPeriod();
|
||||
|
||||
|
||||
// indicate whether JACK & Co should provide output-buffer at ext. port
|
||||
inline bool extOutputEnabled() const
|
||||
@@ -110,28 +98,20 @@ public:
|
||||
bool processEffects();
|
||||
|
||||
// ThreadableJob stuff
|
||||
virtual void doProcessing( sampleFrame * );
|
||||
virtual void doProcessing();
|
||||
virtual bool requiresProcessing() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
enum bufferUsages
|
||||
{
|
||||
NoUsage,
|
||||
FirstBuffer,
|
||||
BothBuffers
|
||||
} ;
|
||||
|
||||
void addPlayHandle( PlayHandle * handle );
|
||||
void removePlayHandle( PlayHandle * handle );
|
||||
|
||||
private:
|
||||
volatile bufferUsages m_bufferUsage;
|
||||
volatile bool m_bufferUsage;
|
||||
|
||||
sampleFrame * m_firstBuffer;
|
||||
sampleFrame * m_secondBuffer;
|
||||
QMutex m_firstBufferLock;
|
||||
QMutex m_secondBufferLock;
|
||||
sampleFrame * m_portBuffer;
|
||||
QMutex m_portBufferLock;
|
||||
|
||||
bool m_extOutputEnabled;
|
||||
fx_ch_t m_nextFxChannel;
|
||||
@@ -140,6 +120,11 @@ private:
|
||||
|
||||
EffectChain * m_effects;
|
||||
|
||||
PlayHandleList m_playHandles;
|
||||
QMutex m_playHandleLock;
|
||||
|
||||
FloatModel * m_volumeModel;
|
||||
FloatModel * m_panningModel;
|
||||
|
||||
friend class Mixer;
|
||||
friend class MixerWorkerThread;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "Model.h"
|
||||
#include "MidiTime.h"
|
||||
#include "ValueBuffer.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
// simple way to map a property of a view to a model
|
||||
#define mapPropertyFromModelPtr(type,getfunc,setfunc,modelname) \
|
||||
@@ -66,6 +66,7 @@ class ControllerConnection;
|
||||
class EXPORT AutomatableModel : public Model, public JournallingObject
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
public:
|
||||
typedef QVector<AutomatableModel *> AutoModelVector;
|
||||
|
||||
|
||||
59
include/BufferManager.h
Normal file
59
include/BufferManager.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* BufferManager.h - A buffer caching/memory management system
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
* Copyright (c) 2006-2014 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 BUFFER_MANAGER_H
|
||||
#define BUFFER_MANAGER_H
|
||||
|
||||
#include "MemoryManager.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "engine.h"
|
||||
#include "Mixer.h"
|
||||
#include <QtCore/QAtomicInt>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
|
||||
const int BM_INITIAL_BUFFERS = 512;
|
||||
//const int BM_INCREMENT = 64;
|
||||
|
||||
class EXPORT BufferManager
|
||||
{
|
||||
public:
|
||||
static void init( fpp_t framesPerPeriod );
|
||||
static sampleFrame * acquire();
|
||||
static void release( sampleFrame * buf );
|
||||
static void refresh();
|
||||
// static void extend( int c );
|
||||
|
||||
private:
|
||||
static sampleFrame ** s_available;
|
||||
static QAtomicInt s_availableIndex;
|
||||
|
||||
static sampleFrame ** s_released;
|
||||
static QAtomicInt s_releasedIndex;
|
||||
// static QReadWriteLock s_mutex;
|
||||
static int s_size;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <QtCore/QVector>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class engine;
|
||||
|
||||
@@ -49,6 +49,7 @@ const QString LOCALE_PATH = "locale/";
|
||||
|
||||
class EXPORT ConfigManager
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
static inline ConfigManager * inst()
|
||||
{
|
||||
|
||||
@@ -32,10 +32,11 @@
|
||||
|
||||
#include "export.h"
|
||||
#include "lmms_basics.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class EXPORT DataFile : public QDomDocument
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum Types
|
||||
{
|
||||
|
||||
@@ -23,14 +23,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DETUNING_HELPER_H
|
||||
#define _DETUNING_HELPER_H
|
||||
#ifndef DETUNING_HELPER_H
|
||||
#define DETUNING_HELPER_H
|
||||
|
||||
#include "InlineAutomation.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class DetuningHelper : public InlineAutomation
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
DetuningHelper() :
|
||||
InlineAutomation()
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "Mixer.h"
|
||||
#include "AutomatableModel.h"
|
||||
#include "TempoSyncKnobModel.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class EffectChain;
|
||||
class EffectControls;
|
||||
@@ -39,6 +39,7 @@ class EffectControls;
|
||||
|
||||
class EXPORT Effect : public Plugin
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
Effect( const Plugin::Descriptor * _desc,
|
||||
Model * _parent,
|
||||
|
||||
@@ -76,7 +76,7 @@ class FxChannel : public ThreadableJob
|
||||
void processed();
|
||||
|
||||
private:
|
||||
virtual void doProcessing( sampleFrame * _working_buffer );
|
||||
virtual void doProcessing();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ class track;
|
||||
|
||||
class EXPORT Instrument : public Plugin
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum Flag
|
||||
{
|
||||
|
||||
@@ -28,16 +28,12 @@
|
||||
#include "PlayHandle.h"
|
||||
#include "Instrument.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "export.h"
|
||||
|
||||
|
||||
class InstrumentPlayHandle : public PlayHandle
|
||||
class EXPORT InstrumentPlayHandle : public PlayHandle
|
||||
{
|
||||
public:
|
||||
InstrumentPlayHandle( Instrument* instrument ) :
|
||||
PlayHandle( TypeInstrumentPlayHandle ),
|
||||
m_instrument( instrument )
|
||||
{
|
||||
}
|
||||
InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack );
|
||||
|
||||
virtual ~InstrumentPlayHandle()
|
||||
{
|
||||
@@ -88,6 +84,7 @@ public:
|
||||
|
||||
private:
|
||||
Instrument* m_instrument;
|
||||
InstrumentTrack * m_instrumentTrack;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ class trackLabelButton;
|
||||
class EXPORT InstrumentTrack : public track, public MidiEventProcessor
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
mapPropertyFromModel(int,getVolume,setVolume,m_volumeModel);
|
||||
public:
|
||||
InstrumentTrack( TrackContainer* tc );
|
||||
@@ -224,7 +225,6 @@ protected slots:
|
||||
|
||||
|
||||
private:
|
||||
AudioPort m_audioPort;
|
||||
MidiPort m_midiPort;
|
||||
|
||||
NotePlayHandle* m_notes[NumKeys];
|
||||
@@ -243,6 +243,9 @@ private:
|
||||
|
||||
FloatModel m_volumeModel;
|
||||
FloatModel m_panningModel;
|
||||
|
||||
AudioPort m_audioPort;
|
||||
|
||||
FloatModel m_pitchModel;
|
||||
IntModel m_pitchRangeModel;
|
||||
IntModel m_effectChannelModel;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "AutomatableModel.h"
|
||||
#include "TempoSyncKnobModel.h"
|
||||
#include "ValueBuffer.h"
|
||||
|
||||
|
||||
typedef struct PortDescription port_desc_t;
|
||||
@@ -44,6 +45,7 @@ public:
|
||||
~LadspaControl();
|
||||
|
||||
LADSPA_Data value();
|
||||
ValueBuffer * valueBuffer();
|
||||
void setValue( LADSPA_Data _value );
|
||||
void setLink( bool _state );
|
||||
|
||||
|
||||
158
include/MemoryManager.h
Normal file
158
include/MemoryManager.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* MemoryManager.h - A lightweight, generic memory manager for LMMS
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki
|
||||
* Copyright (c) 2007-2014 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 MEMORY_MANAGER_H
|
||||
#define MEMORY_MANAGER_H
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
#include <QtCore/QHash>
|
||||
#include "MemoryHelper.h"
|
||||
#include "export.h"
|
||||
|
||||
const int MM_CHUNK_SIZE = 64; // granularity of managed memory
|
||||
const int MM_INITIAL_CHUNKS = 1024 * 1024; // how many chunks to allocate at startup - TODO: make configurable
|
||||
const int MM_INCREMENT_CHUNKS = 16 * 1024; // min. amount of chunks to increment at a time
|
||||
|
||||
struct MemoryPool
|
||||
{
|
||||
void * m_pool;
|
||||
char * m_free;
|
||||
int m_chunks;
|
||||
QMutex m_mutex;
|
||||
|
||||
MemoryPool() :
|
||||
m_pool( NULL ),
|
||||
m_free( NULL ),
|
||||
m_chunks( 0 )
|
||||
{}
|
||||
|
||||
MemoryPool( int chunks ) :
|
||||
m_chunks( chunks )
|
||||
{
|
||||
m_free = (char*) MemoryHelper::alignedMalloc( chunks );
|
||||
memset( m_free, 1, chunks );
|
||||
}
|
||||
|
||||
MemoryPool( const MemoryPool & mp ) :
|
||||
m_pool( mp.m_pool ),
|
||||
m_free( mp.m_free ),
|
||||
m_chunks( mp.m_chunks ),
|
||||
m_mutex()
|
||||
{}
|
||||
|
||||
MemoryPool & operator = ( const MemoryPool & mp )
|
||||
{
|
||||
m_pool = mp.m_pool;
|
||||
m_free = mp.m_free;
|
||||
m_chunks = mp.m_chunks;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void * getChunks( int chunksNeeded );
|
||||
void releaseChunks( void * ptr, int chunks );
|
||||
};
|
||||
|
||||
struct PtrInfo
|
||||
{
|
||||
int chunks;
|
||||
MemoryPool * memPool;
|
||||
};
|
||||
|
||||
typedef QVector<MemoryPool> MemoryPoolVector;
|
||||
typedef QHash<void*, PtrInfo> PointerInfoMap;
|
||||
|
||||
class EXPORT MemoryManager
|
||||
{
|
||||
public:
|
||||
static bool init();
|
||||
static void * alloc( size_t size );
|
||||
static void free( void * ptr );
|
||||
static int extend( int chunks ); // returns index of created pool (for use by alloc)
|
||||
static void cleanup();
|
||||
|
||||
private:
|
||||
static MemoryPoolVector s_memoryPools;
|
||||
static QReadWriteLock s_poolMutex;
|
||||
|
||||
static PointerInfoMap s_pointerInfo;
|
||||
static QMutex s_pointerMutex;
|
||||
};
|
||||
|
||||
|
||||
#define MM_OPERATORS \
|
||||
public: \
|
||||
static void * operator new ( size_t size ) \
|
||||
{ \
|
||||
return MemoryManager::alloc( size ); \
|
||||
} \
|
||||
static void * operator new[] ( size_t size ) \
|
||||
{ \
|
||||
return MemoryManager::alloc( size ); \
|
||||
} \
|
||||
static void operator delete ( void * ptr ) \
|
||||
{ \
|
||||
MemoryManager::free( ptr ); \
|
||||
} \
|
||||
static void operator delete[] ( void * ptr ) \
|
||||
{ \
|
||||
MemoryManager::free( ptr ); \
|
||||
}
|
||||
|
||||
// for use in cases where overriding new/delete isn't a possibility
|
||||
#define MM_ALLOC( type, count ) (type*) MemoryManager::alloc( sizeof( type ) * count )
|
||||
// and just for symmetry...
|
||||
#define MM_FREE( ptr ) MemoryManager::free( ptr )
|
||||
|
||||
|
||||
|
||||
// for debugging purposes
|
||||
|
||||
#define MM_OPERATORS_DEBUG \
|
||||
public: \
|
||||
static void * operator new ( size_t size ) \
|
||||
{ \
|
||||
qDebug( "MM_OPERATORS_DEBUG: new called for %d bytes", size ); \
|
||||
return MemoryManager::alloc( size ); \
|
||||
} \
|
||||
static void * operator new[] ( size_t size ) \
|
||||
{ \
|
||||
qDebug( "MM_OPERATORS_DEBUG: new[] called for %d bytes", size ); \
|
||||
return MemoryManager::alloc( size ); \
|
||||
} \
|
||||
static void operator delete ( void * ptr ) \
|
||||
{ \
|
||||
qDebug( "MM_OPERATORS_DEBUG: delete called for %p", ptr ); \
|
||||
MemoryManager::free( ptr ); \
|
||||
} \
|
||||
static void operator delete[] ( void * ptr ) \
|
||||
{ \
|
||||
qDebug( "MM_OPERATORS_DEBUG: delete[] called for %p", ptr ); \
|
||||
MemoryManager::free( ptr ); \
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -22,8 +22,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MIDI_EVENT_H
|
||||
#define _MIDI_EVENT_H
|
||||
#ifndef MIDI_EVENT_H
|
||||
#define MIDI_EVENT_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include "Midi.h"
|
||||
|
||||
@@ -27,11 +27,12 @@
|
||||
|
||||
#include "MidiEvent.h"
|
||||
#include "MidiTime.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
// all classes being able to process MIDI-events should inherit from this
|
||||
class MidiEventProcessor
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
MidiEventProcessor()
|
||||
{
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace MixHelpers
|
||||
|
||||
bool isSilent( const sampleFrame* src, int frames );
|
||||
|
||||
bool sanitize( sampleFrame * src, int frames );
|
||||
|
||||
/*! \brief Add samples from src to dst */
|
||||
void add( sampleFrame* dst, const sampleFrame* src, int frames );
|
||||
|
||||
@@ -49,6 +51,12 @@ void addMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuff
|
||||
/*! \brief Same as addMultiplied, but sanitize output (strip out infs/nans) */
|
||||
void addSanitizedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames );
|
||||
|
||||
/*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst - sanitized version */
|
||||
void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames );
|
||||
|
||||
/*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst - sanitized version */
|
||||
void addSanitizedMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames );
|
||||
|
||||
/*! \brief Add samples from src multiplied by coeffSrcLeft/coeffSrcRight to dst */
|
||||
void addMultipliedStereo( sampleFrame* dst, const sampleFrame* src, float coeffSrcLeft, float coeffSrcRight, int frames );
|
||||
|
||||
|
||||
@@ -216,20 +216,7 @@ public:
|
||||
|
||||
|
||||
// play-handle stuff
|
||||
bool addPlayHandle( PlayHandle* handle )
|
||||
{
|
||||
if( criticalXRuns() == false )
|
||||
{
|
||||
m_playHandleMutex.lock();
|
||||
m_newPlayHandles.append( handle );
|
||||
m_playHandleMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
delete handle;
|
||||
|
||||
return false;
|
||||
}
|
||||
bool addPlayHandle( PlayHandle* handle );
|
||||
|
||||
void removePlayHandle( PlayHandle* handle );
|
||||
|
||||
@@ -334,11 +321,6 @@ public:
|
||||
}
|
||||
|
||||
// audio-buffer-mgm
|
||||
void bufferToPort( const sampleFrame * _buf,
|
||||
const fpp_t _frames,
|
||||
stereoVolumeVector _volume_vector,
|
||||
AudioPort * _port );
|
||||
|
||||
static void clearAudioBuffer( sampleFrame * _ab,
|
||||
const f_cnt_t _frames,
|
||||
const f_cnt_t _offset = 0 );
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
void addJob( ThreadableJob * _job );
|
||||
|
||||
void run( sampleFrame * _buffer );
|
||||
void run();
|
||||
void wait();
|
||||
|
||||
private:
|
||||
@@ -115,7 +115,6 @@ private:
|
||||
static QWaitCondition * queueReadyWaitCond;
|
||||
static QList<MixerWorkerThread *> workerThreads;
|
||||
|
||||
sampleFrame * m_workingBuf;
|
||||
volatile bool m_quit;
|
||||
|
||||
} ;
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
#include "note.h"
|
||||
#include "PlayHandle.h"
|
||||
#include "track.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
#include <QtCore/QAtomicInt>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
class InstrumentTrack;
|
||||
class NotePlayHandle;
|
||||
@@ -42,6 +44,7 @@ typedef QList<const NotePlayHandle *> ConstNotePlayHandleList;
|
||||
|
||||
class EXPORT NotePlayHandle : public PlayHandle, public note
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
void * m_pluginData;
|
||||
basicFilters<> * m_filter;
|
||||
@@ -56,7 +59,7 @@ public:
|
||||
OriginCount
|
||||
};
|
||||
typedef Origins Origin;
|
||||
|
||||
|
||||
NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
const f_cnt_t offset,
|
||||
const f_cnt_t frames,
|
||||
@@ -64,7 +67,13 @@ public:
|
||||
NotePlayHandle* parent = NULL,
|
||||
int midiEventChannel = -1,
|
||||
Origin origin = OriginPattern );
|
||||
virtual ~NotePlayHandle();
|
||||
virtual ~NotePlayHandle() {}
|
||||
void done();
|
||||
|
||||
void * operator new ( size_t size, void * p )
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
virtual void setVolume( volume_t volume );
|
||||
virtual void setPanning( panning_t panning );
|
||||
@@ -90,8 +99,6 @@ public:
|
||||
return m_frequency;
|
||||
}
|
||||
|
||||
void updateFrequency();
|
||||
|
||||
/*! Returns frequency without pitch wheel influence */
|
||||
float unpitchedFrequency() const
|
||||
{
|
||||
@@ -239,10 +246,15 @@ public:
|
||||
return m_songGlobalParentOffset;
|
||||
}
|
||||
|
||||
void setFrequencyUpdate()
|
||||
{
|
||||
m_frequencyNeedsUpdate = true;
|
||||
}
|
||||
|
||||
private:
|
||||
class BaseDetuning
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
BaseDetuning( DetuningHelper* detuning );
|
||||
|
||||
@@ -262,6 +274,8 @@ private:
|
||||
|
||||
} ;
|
||||
|
||||
void updateFrequency();
|
||||
|
||||
InstrumentTrack* m_instrumentTrack; // needed for calling
|
||||
// InstrumentTrack::playNote
|
||||
f_cnt_t m_frames; // total frames to play
|
||||
@@ -286,7 +300,7 @@ private:
|
||||
bpm_t m_origTempo; // original tempo
|
||||
f_cnt_t m_origFrames; // original m_frames
|
||||
|
||||
const int m_origBaseNote;
|
||||
int m_origBaseNote;
|
||||
|
||||
float m_frequency;
|
||||
float m_unpitchedFrequency;
|
||||
@@ -294,9 +308,37 @@ private:
|
||||
BaseDetuning* m_baseDetuning;
|
||||
MidiTime m_songGlobalParentOffset;
|
||||
|
||||
const int m_midiChannel;
|
||||
const Origin m_origin;
|
||||
int m_midiChannel;
|
||||
Origin m_origin;
|
||||
|
||||
bool m_frequencyNeedsUpdate; // used to update pitch
|
||||
} ;
|
||||
|
||||
|
||||
const int INITIAL_NPH_CACHE = 256;
|
||||
const int NPH_CACHE_INCREMENT = 16;
|
||||
|
||||
class NotePlayHandleManager
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
static void init();
|
||||
static NotePlayHandle * acquire( InstrumentTrack* instrumentTrack,
|
||||
const f_cnt_t offset,
|
||||
const f_cnt_t frames,
|
||||
const note& noteToPlay,
|
||||
NotePlayHandle* parent = NULL,
|
||||
int midiEventChannel = -1,
|
||||
NotePlayHandle::Origin origin = NotePlayHandle::OriginPattern );
|
||||
static void release( NotePlayHandle * nph );
|
||||
static void extend( int i );
|
||||
|
||||
private:
|
||||
static NotePlayHandle ** s_available;
|
||||
static QReadWriteLock s_mutex;
|
||||
static QAtomicInt s_availableIndex;
|
||||
static int s_size;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _OSCILLATOR_H
|
||||
#define _OSCILLATOR_H
|
||||
#ifndef OSCILLATOR_H
|
||||
#define OSCILLATOR_H
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
@@ -43,6 +43,7 @@ class IntModel;
|
||||
|
||||
class EXPORT Oscillator
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum WaveShapes
|
||||
{
|
||||
|
||||
@@ -61,7 +61,8 @@ public:
|
||||
|
||||
public slots:
|
||||
virtual ControllerDialog * createDialog( QWidget * _parent );
|
||||
void handleDestroyedEffect( );
|
||||
void handleDestroyedEffect( );
|
||||
void updateCoeffs();
|
||||
|
||||
protected:
|
||||
// The internal per-controller get-value function
|
||||
@@ -77,6 +78,10 @@ private:
|
||||
static int m_getCount;
|
||||
static int m_loadCount;
|
||||
static bool m_buggedFile;
|
||||
|
||||
float m_attackCoeff;
|
||||
float m_decayCoeff;
|
||||
bool m_coeffNeedsUpdate;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "lmms_basics.h"
|
||||
|
||||
class track;
|
||||
|
||||
class AudioPort;
|
||||
|
||||
class PlayHandle : public ThreadableJob
|
||||
{
|
||||
@@ -48,16 +48,17 @@ public:
|
||||
} ;
|
||||
typedef Types Type;
|
||||
|
||||
PlayHandle( const Type type, f_cnt_t offset = 0 ) :
|
||||
m_type( type ),
|
||||
m_offset( offset ),
|
||||
m_affinity( QThread::currentThread() )
|
||||
PlayHandle( const Type type, f_cnt_t offset = 0 );
|
||||
|
||||
PlayHandle & operator = ( PlayHandle & p )
|
||||
{
|
||||
m_type = p.m_type;
|
||||
m_offset = p.m_offset;
|
||||
m_affinity = p.m_affinity;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~PlayHandle()
|
||||
{
|
||||
}
|
||||
virtual ~PlayHandle();
|
||||
|
||||
virtual bool affinityMatters() const
|
||||
{
|
||||
@@ -75,10 +76,7 @@ public:
|
||||
}
|
||||
|
||||
// required for ThreadableJob
|
||||
virtual void doProcessing( sampleFrame* buffer )
|
||||
{
|
||||
play( buffer );
|
||||
}
|
||||
virtual void doProcessing();
|
||||
|
||||
virtual bool requiresProcessing() const
|
||||
{
|
||||
@@ -98,7 +96,7 @@ public:
|
||||
return m_processingLock.tryLock();
|
||||
}
|
||||
virtual void play( sampleFrame* buffer ) = 0;
|
||||
virtual bool isFinished( void ) const = 0;
|
||||
virtual bool isFinished() const = 0;
|
||||
|
||||
// returns the frameoffset at the start of the playhandle,
|
||||
// ie. how many empty frames should be inserted at the start of the first period
|
||||
@@ -115,12 +113,41 @@ public:
|
||||
|
||||
virtual bool isFromTrack( const track * _track ) const = 0;
|
||||
|
||||
bool usesBuffer() const
|
||||
{
|
||||
return m_usesBuffer;
|
||||
}
|
||||
|
||||
void setUsesBuffer( const bool b )
|
||||
{
|
||||
m_usesBuffer = b;
|
||||
}
|
||||
|
||||
AudioPort * audioPort()
|
||||
{
|
||||
return m_audioPort;
|
||||
}
|
||||
|
||||
void setAudioPort( AudioPort * port )
|
||||
{
|
||||
m_audioPort = port;
|
||||
}
|
||||
|
||||
void releaseBuffer();
|
||||
|
||||
sampleFrame * buffer()
|
||||
{
|
||||
return m_playHandleBuffer;
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
f_cnt_t m_offset;
|
||||
const QThread* m_affinity;
|
||||
QThread* m_affinity;
|
||||
QMutex m_processingLock;
|
||||
sampleFrame * m_playHandleBuffer;
|
||||
bool m_usesBuffer;
|
||||
AudioPort * m_audioPort;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "JournallingObject.h"
|
||||
#include "Model.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
class QWidget;
|
||||
@@ -42,6 +43,7 @@ class AutomatableModel;
|
||||
|
||||
class EXPORT Plugin : public JournallingObject, public Model
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum PluginTypes
|
||||
{
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PRESET_PREVIEW_PLAY_HANDLE_H
|
||||
#define _PRESET_PREVIEW_PLAY_HANDLE_H
|
||||
#ifndef PRESET_PREVIEW_PLAY_HANDLE_H
|
||||
#define PRESET_PREVIEW_PLAY_HANDLE_H
|
||||
|
||||
#include "NotePlayHandle.h"
|
||||
|
||||
|
||||
@@ -31,10 +31,12 @@
|
||||
#include <QObject>
|
||||
#include "lmms_basics.h"
|
||||
#include "lmms_math.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class RingBuffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
public:
|
||||
/** \brief Constructs a ringbuffer of specified size, will not care about samplerate changes
|
||||
* \param size The size of the buffer in frames. The actual size will be size + period size
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#ifndef SAMPLE_BUFFER_H
|
||||
#define SAMPLE_BUFFER_H
|
||||
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QRect>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "lmms_math.h"
|
||||
#include "shared_object.h"
|
||||
#include "Mixer.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
class QPainter;
|
||||
@@ -51,6 +52,7 @@ const f_cnt_t MARGIN[] = { 64, 64, 64, 4, 4 };
|
||||
class EXPORT SampleBuffer : public QObject, public sharedObject
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum LoopMode {
|
||||
LoopOff = 0,
|
||||
@@ -59,6 +61,7 @@ public:
|
||||
};
|
||||
class EXPORT handleState
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
handleState( bool _varying_pitch = false, int interpolation_mode = SRC_LINEAR );
|
||||
virtual ~handleState();
|
||||
@@ -148,21 +151,21 @@ public:
|
||||
|
||||
void setLoopStartFrame( f_cnt_t _start )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_varLock.lockForWrite();
|
||||
m_loopStartFrame = _start;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
void setLoopEndFrame( f_cnt_t _end )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_varLock.lockForWrite();
|
||||
m_loopEndFrame = _end;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
void setAllPointFrames( f_cnt_t _start, f_cnt_t _end, f_cnt_t _loopstart, f_cnt_t _loopend )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_varLock.lockForWrite();
|
||||
m_startFrame = _start;
|
||||
m_endFrame = _end;
|
||||
m_loopStartFrame = _loopstart;
|
||||
@@ -202,14 +205,14 @@ public:
|
||||
|
||||
inline void setFrequency( float _freq )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_varLock.lockForWrite();
|
||||
m_frequency = _freq;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
inline void setSampleRate( sample_rate_t _rate )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_varLock.lockForWrite();
|
||||
m_sampleRate = _rate;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
@@ -264,7 +267,7 @@ public slots:
|
||||
void setEndFrame( const f_cnt_t _e );
|
||||
void setAmplification( float _a );
|
||||
void setReversed( bool _on );
|
||||
|
||||
void sampleRateChanged();
|
||||
|
||||
private:
|
||||
void update( bool _keep_settings = false );
|
||||
@@ -288,7 +291,7 @@ private:
|
||||
sampleFrame * m_origData;
|
||||
f_cnt_t m_origFrames;
|
||||
sampleFrame * m_data;
|
||||
QMutex m_varLock;
|
||||
QReadWriteLock m_varLock;
|
||||
f_cnt_t m_frames;
|
||||
f_cnt_t m_startFrame;
|
||||
f_cnt_t m_endFrame;
|
||||
|
||||
@@ -82,7 +82,6 @@ private:
|
||||
f_cnt_t m_frame;
|
||||
SampleBuffer::handleState m_state;
|
||||
|
||||
AudioPort * m_audioPort;
|
||||
const bool m_ownAudioPort;
|
||||
|
||||
FloatModel m_defaultVolumeModel;
|
||||
|
||||
@@ -147,8 +147,8 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
AudioPort m_audioPort;
|
||||
FloatModel m_volumeModel;
|
||||
AudioPort m_audioPort;
|
||||
|
||||
|
||||
friend class SampleTrackView;
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _THREADABLE_JOB_H
|
||||
#define _THREADABLE_JOB_H
|
||||
#ifndef THREADABLE_JOB_H
|
||||
#define THREADABLE_JOB_H
|
||||
|
||||
#include <QtCore/QAtomicInt>
|
||||
|
||||
@@ -67,11 +67,11 @@ public:
|
||||
m_state = Done;
|
||||
}
|
||||
|
||||
void process( sampleFrame* workingBuffer = NULL )
|
||||
void process()
|
||||
{
|
||||
if( m_state.testAndSetOrdered( Queued, InProgress ) )
|
||||
{
|
||||
doProcessing( workingBuffer );
|
||||
doProcessing();
|
||||
m_state = Done;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
|
||||
|
||||
protected:
|
||||
virtual void doProcessing( sampleFrame* workingBuffer) = 0;
|
||||
virtual void doProcessing() = 0;
|
||||
|
||||
QAtomicInt m_state;
|
||||
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
#include <QtGlobal>
|
||||
#include "interpolation.h"
|
||||
#include <string.h>
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class ValueBuffer
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
ValueBuffer()
|
||||
{
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "templates.h"
|
||||
#include "lmms_constants.h"
|
||||
#include "interpolation.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
//#include <iostream>
|
||||
//#include <cstdlib>
|
||||
@@ -50,6 +51,7 @@
|
||||
template<ch_cnt_t CHANNELS/* = DEFAULT_CHANNELS*/>
|
||||
class basicFilters
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum FilterTypes
|
||||
{
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ENGINE_H
|
||||
#define _ENGINE_H
|
||||
#ifndef ENGINE_H
|
||||
#define ENGINE_H
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
#include <QtCore/QMap>
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ const int TCO_BORDER_WIDTH = 2;
|
||||
class trackContentObject : public Model, public JournallingObject
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel);
|
||||
mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel);
|
||||
public:
|
||||
@@ -406,6 +407,7 @@ signals:
|
||||
class EXPORT track : public Model, public JournallingObject
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel);
|
||||
mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel);
|
||||
public:
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#include "EffectChain.h"
|
||||
#include "AutomationPattern.h"
|
||||
#include "ControllerConnection.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "ValueBuffer.h"
|
||||
|
||||
#include "embed.cpp"
|
||||
|
||||
@@ -155,9 +157,7 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf,
|
||||
}
|
||||
|
||||
// Copy the LMMS audio buffer to the LADSPA input buffer and initialize
|
||||
// the control ports. Need to change this to handle non-in-place-broken
|
||||
// plugins--would speed things up to use the same buffer for both
|
||||
// LMMS and LADSPA.
|
||||
// the control ports.
|
||||
ch_cnt_t channel = 0;
|
||||
for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
|
||||
{
|
||||
@@ -176,18 +176,28 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf,
|
||||
++channel;
|
||||
break;
|
||||
case AUDIO_RATE_INPUT:
|
||||
pp->value = static_cast<LADSPA_Data>(
|
||||
pp->control->value() / pp->scale );
|
||||
// This only supports control rate ports, so the audio rates are
|
||||
// treated as though they were control rate by setting the
|
||||
// port buffer to all the same value.
|
||||
for( fpp_t frame = 0;
|
||||
frame < frames; ++frame )
|
||||
{
|
||||
ValueBuffer * vb = pp->control->valueBuffer();
|
||||
if( vb )
|
||||
{
|
||||
pp->buffer[frame] =
|
||||
pp->value;
|
||||
memcpy( pp->buffer, vb->values(), frames * sizeof(float) );
|
||||
}
|
||||
else
|
||||
{
|
||||
pp->value = static_cast<LADSPA_Data>(
|
||||
pp->control->value() / pp->scale );
|
||||
// This only supports control rate ports, so the audio rates are
|
||||
// treated as though they were control rate by setting the
|
||||
// port buffer to all the same value.
|
||||
for( fpp_t frame = 0;
|
||||
frame < frames; ++frame )
|
||||
{
|
||||
pp->buffer[frame] =
|
||||
pp->value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONTROL_RATE_INPUT:
|
||||
if( pp->control == NULL )
|
||||
{
|
||||
@@ -208,6 +218,7 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process the buffers.
|
||||
for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
|
||||
{
|
||||
@@ -287,9 +298,17 @@ void LadspaEffect::pluginInstantiation()
|
||||
int effect_channels = manager->getDescription( m_key )->inputChannels;
|
||||
setProcessorCount( lmms_chnls / effect_channels );
|
||||
|
||||
// get inPlaceBroken property
|
||||
m_inPlaceBroken = manager->isInplaceBroken( m_key );
|
||||
|
||||
// Categorize the ports, and create the buffers.
|
||||
m_portCount = manager->getPortCount( m_key );
|
||||
|
||||
int inputch = 0;
|
||||
int outputch = 0;
|
||||
LADSPA_Data * inbuf [2];
|
||||
inbuf[0] = NULL;
|
||||
inbuf[1] = NULL;
|
||||
for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
|
||||
{
|
||||
multi_proc_t ports;
|
||||
@@ -301,39 +320,48 @@ void LadspaEffect::pluginInstantiation()
|
||||
p->proc = proc;
|
||||
p->port_id = port;
|
||||
p->control = NULL;
|
||||
p->buffer = NULL;
|
||||
|
||||
// Determine the port's category.
|
||||
if( manager->isPortAudio( m_key, port ) )
|
||||
{
|
||||
// Nasty manual memory management--was having difficulty
|
||||
// with some prepackaged plugins that were segfaulting
|
||||
// during cleanup. It was easier to troubleshoot with the
|
||||
// memory management all taking place in one file.
|
||||
p->buffer =
|
||||
new LADSPA_Data[engine::mixer()->framesPerPeriod()];
|
||||
|
||||
if( p->name.toUpper().contains( "IN" ) &&
|
||||
manager->isPortInput( m_key, port ) )
|
||||
{
|
||||
p->rate = CHANNEL_IN;
|
||||
p->buffer = MM_ALLOC( LADSPA_Data, engine::mixer()->framesPerPeriod() );
|
||||
inbuf[ inputch ] = p->buffer;
|
||||
inputch++;
|
||||
}
|
||||
else if( p->name.toUpper().contains( "OUT" ) &&
|
||||
manager->isPortOutput( m_key, port ) )
|
||||
{
|
||||
p->rate = CHANNEL_OUT;
|
||||
if( ! m_inPlaceBroken && inbuf[ outputch ] )
|
||||
{
|
||||
p->buffer = inbuf[ outputch ];
|
||||
outputch++;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->buffer = MM_ALLOC( LADSPA_Data, engine::mixer()->framesPerPeriod() );
|
||||
m_inPlaceBroken = true;
|
||||
}
|
||||
}
|
||||
else if( manager->isPortInput( m_key, port ) )
|
||||
{
|
||||
p->rate = AUDIO_RATE_INPUT;
|
||||
p->buffer = MM_ALLOC( LADSPA_Data, engine::mixer()->framesPerPeriod() );
|
||||
}
|
||||
else
|
||||
{
|
||||
p->rate = AUDIO_RATE_OUTPUT;
|
||||
p->buffer = MM_ALLOC( LADSPA_Data, engine::mixer()->framesPerPeriod() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p->buffer = new LADSPA_Data[1];
|
||||
p->buffer = MM_ALLOC( LADSPA_Data, 1 );
|
||||
|
||||
if( manager->isPortInput( m_key, port ) )
|
||||
{
|
||||
@@ -526,7 +554,10 @@ void LadspaEffect::pluginDestruction()
|
||||
for( int port = 0; port < m_portCount; port++ )
|
||||
{
|
||||
port_desc_t * pp = m_ports.at( proc ).at( port );
|
||||
delete[] pp->buffer;
|
||||
if( m_inPlaceBroken || pp->rate != CHANNEL_OUT )
|
||||
{
|
||||
if( pp->buffer) MM_FREE( pp->buffer );
|
||||
}
|
||||
delete pp;
|
||||
}
|
||||
m_ports[proc].clear();
|
||||
@@ -550,7 +581,6 @@ sample_rate_t LadspaEffect::maxSamplerate( const QString & _name )
|
||||
__buggy_plugins["C* AmpVTS"] = 88200;
|
||||
__buggy_plugins["Chorus2"] = 44100;
|
||||
__buggy_plugins["Notch Filter"] = 96000;
|
||||
__buggy_plugins["Freeverb"] = 44100;
|
||||
__buggy_plugins["TAP Reflector"] = 192000;
|
||||
}
|
||||
if( __buggy_plugins.contains( _name ) )
|
||||
|
||||
@@ -76,6 +76,7 @@ private:
|
||||
sample_rate_t m_maxSampleRate;
|
||||
ladspa_key_t m_key;
|
||||
int m_portCount;
|
||||
bool m_inPlaceBroken;
|
||||
|
||||
const LADSPA_Descriptor * m_descriptor;
|
||||
QVector<LADSPA_Handle> m_handles;
|
||||
|
||||
@@ -6,33 +6,34 @@
|
||||
|
||||
#include "revmodel.h"
|
||||
|
||||
revmodel::revmodel()
|
||||
revmodel::revmodel( float sampleRatio ) :
|
||||
m_sampleRatio( sampleRatio )
|
||||
{
|
||||
// Tie the components to their buffers
|
||||
combL[0].setbuffer(bufcombL1,combtuningL1);
|
||||
combR[0].setbuffer(bufcombR1,combtuningR1);
|
||||
combL[1].setbuffer(bufcombL2,combtuningL2);
|
||||
combR[1].setbuffer(bufcombR2,combtuningR2);
|
||||
combL[2].setbuffer(bufcombL3,combtuningL3);
|
||||
combR[2].setbuffer(bufcombR3,combtuningR3);
|
||||
combL[3].setbuffer(bufcombL4,combtuningL4);
|
||||
combR[3].setbuffer(bufcombR4,combtuningR4);
|
||||
combL[4].setbuffer(bufcombL5,combtuningL5);
|
||||
combR[4].setbuffer(bufcombR5,combtuningR5);
|
||||
combL[5].setbuffer(bufcombL6,combtuningL6);
|
||||
combR[5].setbuffer(bufcombR6,combtuningR6);
|
||||
combL[6].setbuffer(bufcombL7,combtuningL7);
|
||||
combR[6].setbuffer(bufcombR7,combtuningR7);
|
||||
combL[7].setbuffer(bufcombL8,combtuningL8);
|
||||
combR[7].setbuffer(bufcombR8,combtuningR8);
|
||||
allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
|
||||
allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
|
||||
allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
|
||||
allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
|
||||
allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
|
||||
allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
|
||||
allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
|
||||
allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
|
||||
combL[0].setbuffer(bufcombL1,static_cast<int>( combtuningL1 * m_sampleRatio ));
|
||||
combR[0].setbuffer(bufcombR1,static_cast<int>( combtuningR1 * m_sampleRatio ));
|
||||
combL[1].setbuffer(bufcombL2,static_cast<int>( combtuningL2 * m_sampleRatio ));
|
||||
combR[1].setbuffer(bufcombR2,static_cast<int>( combtuningR2 * m_sampleRatio ));
|
||||
combL[2].setbuffer(bufcombL3,static_cast<int>( combtuningL3 * m_sampleRatio ));
|
||||
combR[2].setbuffer(bufcombR3,static_cast<int>( combtuningR3 * m_sampleRatio ));
|
||||
combL[3].setbuffer(bufcombL4,static_cast<int>( combtuningL4 * m_sampleRatio ));
|
||||
combR[3].setbuffer(bufcombR4,static_cast<int>( combtuningR4 * m_sampleRatio ));
|
||||
combL[4].setbuffer(bufcombL5,static_cast<int>( combtuningL5 * m_sampleRatio ));
|
||||
combR[4].setbuffer(bufcombR5,static_cast<int>( combtuningR5 * m_sampleRatio ));
|
||||
combL[5].setbuffer(bufcombL6,static_cast<int>( combtuningL6 * m_sampleRatio ));
|
||||
combR[5].setbuffer(bufcombR6,static_cast<int>( combtuningR6 * m_sampleRatio ));
|
||||
combL[6].setbuffer(bufcombL7,static_cast<int>( combtuningL7 * m_sampleRatio ));
|
||||
combR[6].setbuffer(bufcombR7,static_cast<int>( combtuningR7 * m_sampleRatio ));
|
||||
combL[7].setbuffer(bufcombL8,static_cast<int>( combtuningL8 * m_sampleRatio ));
|
||||
combR[7].setbuffer(bufcombR8,static_cast<int>( combtuningR8 * m_sampleRatio ));
|
||||
allpassL[0].setbuffer(bufallpassL1,static_cast<int>( allpasstuningL1 * m_sampleRatio ));
|
||||
allpassR[0].setbuffer(bufallpassR1,static_cast<int>( allpasstuningR1 * m_sampleRatio ));
|
||||
allpassL[1].setbuffer(bufallpassL2,static_cast<int>( allpasstuningL2 * m_sampleRatio ));
|
||||
allpassR[1].setbuffer(bufallpassR2,static_cast<int>( allpasstuningR2 * m_sampleRatio ));
|
||||
allpassL[2].setbuffer(bufallpassL3,static_cast<int>( allpasstuningL3 * m_sampleRatio ));
|
||||
allpassR[2].setbuffer(bufallpassR3,static_cast<int>( allpasstuningR3 * m_sampleRatio ));
|
||||
allpassL[3].setbuffer(bufallpassL4,static_cast<int>( allpasstuningL4 * m_sampleRatio ));
|
||||
allpassR[3].setbuffer(bufallpassR4,static_cast<int>( allpasstuningR4 * m_sampleRatio ));
|
||||
|
||||
// Set default values
|
||||
allpassL[0].setfeedback(0.5f);
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
#include "allpass.h"
|
||||
#include "tuning.h"
|
||||
|
||||
const int maxSampleRatio = 18; // enough for largest possible samplerate, 8 * 96000
|
||||
|
||||
class revmodel
|
||||
{
|
||||
public:
|
||||
revmodel();
|
||||
revmodel( float sampleRatio );
|
||||
void mute();
|
||||
void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
|
||||
void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
|
||||
@@ -40,6 +42,8 @@ private:
|
||||
float dry;
|
||||
float width;
|
||||
float mode;
|
||||
|
||||
float m_sampleRatio;
|
||||
|
||||
// The following are all declared inline
|
||||
// to remove the need for dynamic allocation
|
||||
@@ -54,32 +58,32 @@ private:
|
||||
allpass allpassR[numallpasses];
|
||||
|
||||
// Buffers for the combs
|
||||
float bufcombL1[combtuningL1];
|
||||
float bufcombR1[combtuningR1];
|
||||
float bufcombL2[combtuningL2];
|
||||
float bufcombR2[combtuningR2];
|
||||
float bufcombL3[combtuningL3];
|
||||
float bufcombR3[combtuningR3];
|
||||
float bufcombL4[combtuningL4];
|
||||
float bufcombR4[combtuningR4];
|
||||
float bufcombL5[combtuningL5];
|
||||
float bufcombR5[combtuningR5];
|
||||
float bufcombL6[combtuningL6];
|
||||
float bufcombR6[combtuningR6];
|
||||
float bufcombL7[combtuningL7];
|
||||
float bufcombR7[combtuningR7];
|
||||
float bufcombL8[combtuningL8];
|
||||
float bufcombR8[combtuningR8];
|
||||
float bufcombL1[combtuningL1 * maxSampleRatio];
|
||||
float bufcombR1[combtuningR1 * maxSampleRatio];
|
||||
float bufcombL2[combtuningL2 * maxSampleRatio];
|
||||
float bufcombR2[combtuningR2 * maxSampleRatio];
|
||||
float bufcombL3[combtuningL3 * maxSampleRatio];
|
||||
float bufcombR3[combtuningR3 * maxSampleRatio];
|
||||
float bufcombL4[combtuningL4 * maxSampleRatio];
|
||||
float bufcombR4[ combtuningR4 * maxSampleRatio ];
|
||||
float bufcombL5[ combtuningL5 * maxSampleRatio ];
|
||||
float bufcombR5[ combtuningR5 * maxSampleRatio ];
|
||||
float bufcombL6[ combtuningL6 * maxSampleRatio ];
|
||||
float bufcombR6[ combtuningR6 * maxSampleRatio ];
|
||||
float bufcombL7[ combtuningL7 * maxSampleRatio ];
|
||||
float bufcombR7[ combtuningR7 * maxSampleRatio ];
|
||||
float bufcombL8[ combtuningL8 * maxSampleRatio ];
|
||||
float bufcombR8[ combtuningR8 * maxSampleRatio ];
|
||||
|
||||
// Buffers for the allpasses
|
||||
float bufallpassL1[allpasstuningL1];
|
||||
float bufallpassR1[allpasstuningR1];
|
||||
float bufallpassL2[allpasstuningL2];
|
||||
float bufallpassR2[allpasstuningR2];
|
||||
float bufallpassL3[allpasstuningL3];
|
||||
float bufallpassR3[allpasstuningR3];
|
||||
float bufallpassL4[allpasstuningL4];
|
||||
float bufallpassR4[allpasstuningR4];
|
||||
float bufallpassL1[ allpasstuningL1 * maxSampleRatio ];
|
||||
float bufallpassR1[ allpasstuningR1 * maxSampleRatio ];
|
||||
float bufallpassL2[ allpasstuningL2 * maxSampleRatio ];
|
||||
float bufallpassR2[ allpasstuningR2 * maxSampleRatio ];
|
||||
float bufallpassL3[ allpasstuningL3 * maxSampleRatio ];
|
||||
float bufallpassR3[ allpasstuningR3 * maxSampleRatio ];
|
||||
float bufallpassL4[ allpasstuningL4 * maxSampleRatio ];
|
||||
float bufallpassR4[ allpasstuningR4 * maxSampleRatio ];
|
||||
};
|
||||
|
||||
#endif//_revmodel_
|
||||
|
||||
@@ -57,14 +57,13 @@ class Freeverb3 : public CMT_PluginInstance, public revmodel {
|
||||
public:
|
||||
|
||||
Freeverb3(const LADSPA_Descriptor *, unsigned long lSampleRate)
|
||||
: CMT_PluginInstance(FV_NumPorts) {
|
||||
/* Richard's note 17/5/2000. Hmm - not sure I like the fact that
|
||||
lSampleRate isn't actually used in this function! */
|
||||
}
|
||||
: CMT_PluginInstance(FV_NumPorts),
|
||||
revmodel( (float) lSampleRate / 44100.0f )
|
||||
{}
|
||||
|
||||
friend void activateFreeverb3(LADSPA_Handle Instance);
|
||||
friend void runFreeverb3(LADSPA_Handle Instance,
|
||||
unsigned long SampleCount);
|
||||
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -179,6 +179,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( _working_buffer, 0, ( frames + offset ) * sizeof( sampleFrame ) );
|
||||
emit isPlaying( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _BIT_INVADER_H
|
||||
#define _BIT_INVADER_H
|
||||
#ifndef BIT_INVADER_H
|
||||
#define BIT_INVADER_H
|
||||
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
@@ -33,12 +33,14 @@
|
||||
#include "knob.h"
|
||||
#include "pixmap_button.h"
|
||||
#include "led_checkbox.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class oscillator;
|
||||
class bitInvaderView;
|
||||
|
||||
class bSynth
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
bSynth( float * sample, int length, NotePlayHandle * _nph,
|
||||
bool _interpolation, float factor,
|
||||
|
||||
@@ -180,7 +180,7 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D
|
||||
fDescriptor->activate(fHandle);
|
||||
|
||||
// we need a play-handle which cares for calling play()
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, instrumentTrack );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
|
||||
connect(engine::mixer(), SIGNAL(sampleRateChanged()), this, SLOT(sampleRateChanged()));
|
||||
|
||||
@@ -31,11 +31,13 @@
|
||||
|
||||
#include "lmms_math.h"
|
||||
#include "interpolation.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
template<class FX = DspEffectLibrary::StereoBypass>
|
||||
class KickerOsc
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
KickerOsc( const FX & fx, const float start, const float end, const float noise, const float offset,
|
||||
const float slope, const float env, const float diststart, const float distend, const float length ) :
|
||||
|
||||
@@ -353,7 +353,7 @@ lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) :
|
||||
|
||||
filterChanged();
|
||||
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrumentTrack );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ class MonstroView;
|
||||
|
||||
class MonstroSynth
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph );
|
||||
virtual ~MonstroSynth();
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "TempoSyncKnob.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "pixmap_button.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
#define makeknob( name, x, y, hint, unit, oname ) \
|
||||
@@ -80,6 +81,7 @@ class NesInstrument;
|
||||
|
||||
class NesObject
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
NesObject( NesInstrument * nes, const sample_rate_t samplerate, NotePlayHandle * nph );
|
||||
virtual ~NesObject();
|
||||
|
||||
@@ -138,7 +138,7 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) :
|
||||
trem_depth_mdl(false, this, tr( "Tremolo Depth" ) )
|
||||
{
|
||||
// Connect the plugin to the mixer...
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
|
||||
// Voices are laid out in a funny way...
|
||||
|
||||
@@ -75,6 +75,7 @@ const float CENT = 1.0f / 1200.0f;
|
||||
class OscillatorObject : public Model
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
private:
|
||||
int m_numOscillators;
|
||||
IntModel m_waveShape;
|
||||
@@ -149,6 +150,7 @@ private:
|
||||
|
||||
struct oscPtr
|
||||
{
|
||||
MM_OPERATORS
|
||||
Oscillator * oscLeft;
|
||||
Oscillator * oscRight;
|
||||
} ;
|
||||
@@ -180,6 +182,7 @@ private:
|
||||
|
||||
struct OscillatorKnobs
|
||||
{
|
||||
MM_OPERATORS
|
||||
OscillatorKnobs(
|
||||
knob * h,
|
||||
knob * v,
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
#include "gb_apu/Gb_Apu.h"
|
||||
#include "gb_apu/Multi_Buffer.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class Basic_Gb_Apu {
|
||||
MM_OPERATORS
|
||||
public:
|
||||
Basic_Gb_Apu();
|
||||
~Basic_Gb_Apu();
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _PATMAN_H_
|
||||
#define _PATMAN_H_
|
||||
#ifndef PATMAN_H_
|
||||
#define PATMAN_H_
|
||||
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "AutomatableModel.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class pixmapButton;
|
||||
|
||||
@@ -79,6 +79,7 @@ public slots:
|
||||
private:
|
||||
typedef struct
|
||||
{
|
||||
MM_OPERATORS
|
||||
SampleBuffer::handleState* state;
|
||||
bool tuned;
|
||||
SampleBuffer* sample;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
@@ -130,8 +130,10 @@ bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf,
|
||||
}
|
||||
|
||||
float curRMS = sqrt_neg( sum / _frames );
|
||||
const float tres = c.m_tresholdModel.value();
|
||||
const float amount = c.m_amountModel.value() * c.m_amountMultModel.value();
|
||||
m_lastSample = c.m_baseModel.value() + amount * curRMS;
|
||||
curRMS = qAbs( curRMS ) < tres ? 0.0f : curRMS;
|
||||
m_lastSample = qBound( 0.0f, c.m_baseModel.value() + amount * curRMS, 1.0f );
|
||||
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ PeakControllerEffectControlDialog::PeakControllerEffectControlDialog(
|
||||
setPalette( pal );
|
||||
|
||||
QVBoxLayout * tl = new QVBoxLayout( this );
|
||||
tl->setContentsMargins( 5, 30, 8, 8 );
|
||||
tl->setContentsMargins( 5, 30, 5, 8 );
|
||||
|
||||
QHBoxLayout * l = new QHBoxLayout;
|
||||
l->setSpacing( 4 );
|
||||
@@ -74,12 +74,18 @@ PeakControllerEffectControlDialog::PeakControllerEffectControlDialog(
|
||||
m_decayKnob->setLabel( tr( "DCAY" ) );
|
||||
m_decayKnob->setModel( &_controls->m_decayModel );
|
||||
m_decayKnob->setHintText( tr( "Release:" ) + " ", "" );
|
||||
|
||||
m_tresholdKnob = new knob( knobBright_26, this );
|
||||
m_tresholdKnob->setLabel( tr( "TRES" ) );
|
||||
m_tresholdKnob->setModel( &_controls->m_tresholdModel );
|
||||
m_tresholdKnob->setHintText( tr( "Treshold:" ) + " ", "" );
|
||||
|
||||
l->addWidget( m_baseKnob );
|
||||
l->addWidget( m_amountKnob );
|
||||
l->addWidget( m_amountMultKnob );
|
||||
l->addWidget( m_attackKnob );
|
||||
l->addWidget( m_decayKnob );
|
||||
l->addWidget( m_tresholdKnob );
|
||||
l->addStretch(); // expand, so other widgets have minimum width
|
||||
tl->addLayout( l );
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ protected:
|
||||
knob * m_amountKnob;
|
||||
knob * m_attackKnob;
|
||||
knob * m_decayKnob;
|
||||
knob * m_tresholdKnob;
|
||||
ledCheckBox * m_muteLed;
|
||||
|
||||
ledCheckBox * m_absLed;
|
||||
|
||||
@@ -41,6 +41,7 @@ PeakControllerEffectControls( PeakControllerEffect * _eff ) :
|
||||
m_amountModel( 1.0, -1.0, 1.0, 0.005, this, tr( "Modulation amount" ) ),
|
||||
m_attackModel( 0, 0, 0.999, 0.001, this, tr( "Attack" ) ),
|
||||
m_decayModel( 0, 0, 0.999, 0.001, this, tr( "Release" ) ),
|
||||
m_tresholdModel( 0, 0, 1.0, 0.001, this, tr( "Treshold" ) ),
|
||||
m_muteModel( false, this, tr( "Mute output" ) ),
|
||||
m_absModel( true, this, tr("Abs Value") ),
|
||||
m_amountMultModel( 1.0, 0, 32, 0.2, this, tr("Amount Multiplicator") )
|
||||
@@ -61,6 +62,8 @@ void PeakControllerEffectControls::loadSettings( const QDomElement & _this )
|
||||
|
||||
m_absModel.loadSettings( _this, "abs" );
|
||||
m_amountMultModel.loadSettings( _this, "amountmult" );
|
||||
|
||||
m_tresholdModel.loadSettings( _this, "treshold" );
|
||||
|
||||
/*If the peak controller effect is NOT loaded from project,
|
||||
* m_effectId stored is useless.
|
||||
@@ -102,6 +105,8 @@ void PeakControllerEffectControls::saveSettings( QDomDocument & _doc,
|
||||
|
||||
m_absModel.saveSettings( _doc, _this, "abs" );
|
||||
m_amountMultModel.saveSettings( _doc, _this, "amountmult" );
|
||||
|
||||
m_tresholdModel.saveSettings( _doc, _this, "treshold" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ private:
|
||||
FloatModel m_amountModel;
|
||||
FloatModel m_attackModel;
|
||||
FloatModel m_decayModel;
|
||||
FloatModel m_tresholdModel;
|
||||
BoolModel m_muteModel;
|
||||
BoolModel m_absModel;
|
||||
FloatModel m_amountMultModel;
|
||||
|
||||
@@ -118,7 +118,7 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) :
|
||||
// everytime we load a new soundfont.
|
||||
m_synth = new_fluid_synth( m_settings );
|
||||
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
|
||||
loadFile( ConfigManager::inst()->defaultSoundfont() );
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "led_checkbox.h"
|
||||
#include "fluidsynth.h"
|
||||
#include "SampleBuffer.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
class sf2InstrumentView;
|
||||
class sf2Font;
|
||||
@@ -173,6 +174,7 @@ signals:
|
||||
// A soundfont in our font-map
|
||||
class sf2Font
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
sf2Font( fluid_sfont_t * f ) :
|
||||
fluidFont( f ),
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "graph.h"
|
||||
#include "pixmap_button.h"
|
||||
#include "led_checkbox.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
enum SfxrWaves
|
||||
@@ -69,6 +70,7 @@ class sfxrInstrument;
|
||||
|
||||
class SfxrSynth
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
SfxrSynth( const sfxrInstrument * s );
|
||||
virtual ~SfxrSynth();
|
||||
|
||||
@@ -41,6 +41,7 @@ class pixmapButton;
|
||||
class voiceObject : public Model
|
||||
{
|
||||
Q_OBJECT
|
||||
MM_OPERATORS
|
||||
public:
|
||||
enum WaveForm {
|
||||
SquareWave = 0,
|
||||
|
||||
@@ -43,6 +43,7 @@ const int NUM_OF_OSCILLATORS = 3;
|
||||
|
||||
class OscillatorObject : public Model
|
||||
{
|
||||
MM_OPERATORS
|
||||
Q_OBJECT
|
||||
public:
|
||||
OscillatorObject( Model * _parent, int _idx );
|
||||
@@ -123,6 +124,7 @@ private:
|
||||
|
||||
struct oscPtr
|
||||
{
|
||||
MM_OPERATORS
|
||||
Oscillator * oscLeft;
|
||||
Oscillator * oscRight;
|
||||
} ;
|
||||
@@ -150,6 +152,7 @@ private:
|
||||
|
||||
struct OscillatorKnobs
|
||||
{
|
||||
MM_OPERATORS
|
||||
OscillatorKnobs( knob * v,
|
||||
knob * p,
|
||||
knob * c,
|
||||
|
||||
@@ -82,7 +82,7 @@ vestigeInstrument::vestigeInstrument( InstrumentTrack * _instrument_track ) :
|
||||
p_subWindow( NULL )
|
||||
{
|
||||
// now we need a play-handle which cares for calling play()
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,12 @@
|
||||
#include <QVector>
|
||||
|
||||
#include "vibrating_string.h"
|
||||
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
class stringContainer
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
stringContainer(const float _pitch,
|
||||
const sample_rate_t _sample_rate,
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "NotePlayHandle.h"
|
||||
#include "pixmap_button.h"
|
||||
#include <samplerate.h>
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
#define makeknob( name, x, y, hint, unit, oname ) \
|
||||
@@ -80,6 +81,7 @@ class WatsynInstrument;
|
||||
|
||||
class WatsynObject
|
||||
{
|
||||
MM_OPERATORS
|
||||
public:
|
||||
WatsynObject( float * _A1wave, float * _A2wave,
|
||||
float * _B1wave, float * _B2wave,
|
||||
|
||||
@@ -129,7 +129,7 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument(
|
||||
connect( &m_resBandwidthModel, SIGNAL( dataChanged() ), this, SLOT( updateResBandwidth() ) );
|
||||
|
||||
// now we need a play-handle which cares for calling play()
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrumentTrack );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ),
|
||||
|
||||
110
src/core/BufferManager.cpp
Normal file
110
src/core/BufferManager.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* BufferManager.cpp - A buffer caching/memory management system
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
* Copyright (c) 2006-2014 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 "BufferManager.h"
|
||||
|
||||
|
||||
sampleFrame ** BufferManager::s_available;
|
||||
QAtomicInt BufferManager::s_availableIndex = 0;
|
||||
sampleFrame ** BufferManager::s_released;
|
||||
QAtomicInt BufferManager::s_releasedIndex = 0;
|
||||
//QReadWriteLock BufferManager::s_mutex;
|
||||
int BufferManager::s_size;
|
||||
|
||||
|
||||
void BufferManager::init( fpp_t framesPerPeriod )
|
||||
{
|
||||
s_available = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS );
|
||||
s_released = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS );
|
||||
|
||||
int c = framesPerPeriod * BM_INITIAL_BUFFERS;
|
||||
sampleFrame * b = MM_ALLOC( sampleFrame, c );
|
||||
|
||||
for( int i = 0; i < BM_INITIAL_BUFFERS; ++i )
|
||||
{
|
||||
s_available[ i ] = b;
|
||||
b += framesPerPeriod;
|
||||
}
|
||||
s_availableIndex = BM_INITIAL_BUFFERS - 1;
|
||||
s_size = BM_INITIAL_BUFFERS;
|
||||
}
|
||||
|
||||
|
||||
sampleFrame * BufferManager::acquire()
|
||||
{
|
||||
if( s_availableIndex < 0 )
|
||||
{
|
||||
qFatal( "BufferManager: out of buffers" );
|
||||
}
|
||||
|
||||
int i = s_availableIndex.fetchAndAddOrdered( -1 );
|
||||
sampleFrame * b = s_available[ i ];
|
||||
|
||||
//qDebug( "acquired buffer: %p - index %d", b, i );
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
void BufferManager::release( sampleFrame * buf )
|
||||
{
|
||||
int i = s_releasedIndex.fetchAndAddOrdered( 1 );
|
||||
s_released[ i ] = buf;
|
||||
//qDebug( "released buffer: %p - index %d", buf, i );
|
||||
}
|
||||
|
||||
|
||||
void BufferManager::refresh() // non-threadsafe, hence it's called periodically from mixer at a time when no other threads can interfere
|
||||
{
|
||||
if( s_releasedIndex == 0 ) return;
|
||||
//qDebug( "refresh: %d buffers", int( s_releasedIndex ) );
|
||||
|
||||
int j = s_availableIndex;
|
||||
for( int i = 0; i < s_releasedIndex; ++i )
|
||||
{
|
||||
++j;
|
||||
s_available[ j ] = s_released[ i ];
|
||||
}
|
||||
s_availableIndex = j;
|
||||
s_releasedIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
/* // non-extensible for now
|
||||
void BufferManager::extend( int c )
|
||||
{
|
||||
s_size += c;
|
||||
sampleFrame ** tmp = MM_ALLOC( sampleFrame*, s_size );
|
||||
MM_FREE( s_available );
|
||||
s_available = tmp;
|
||||
|
||||
int cc = c * engine::mixer()->framesPerPeriod();
|
||||
sampleFrame * b = MM_ALLOC( sampleFrame, cc );
|
||||
|
||||
for( int i = 0; i < c; ++i )
|
||||
{
|
||||
s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = b;
|
||||
b += engine::mixer()->framesPerPeriod();
|
||||
}
|
||||
}*/
|
||||
@@ -31,7 +31,8 @@
|
||||
#include "engine.h"
|
||||
#include "debug.h"
|
||||
#include "DummyEffect.h"
|
||||
|
||||
#include "MixHelpers.h"
|
||||
#include "song.h"
|
||||
|
||||
|
||||
EffectChain::EffectChain( Model * _parent ) :
|
||||
@@ -194,6 +195,11 @@ bool EffectChain::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, b
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const bool exporting = engine::getSong()->isExporting();
|
||||
if( exporting ) // strip infs/nans if exporting
|
||||
{
|
||||
MixHelpers::sanitize( _buf, _frames );
|
||||
}
|
||||
|
||||
bool moreEffects = false;
|
||||
for( EffectList::Iterator it = m_effects.begin(); it != m_effects.end(); ++it )
|
||||
@@ -201,6 +207,10 @@ bool EffectChain::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, b
|
||||
if( hasInputNoise || ( *it )->isRunning() )
|
||||
{
|
||||
moreEffects |= ( *it )->processAudioBuffer( _buf, _frames );
|
||||
if( exporting ) // strip infs/nans if exporting
|
||||
{
|
||||
MixHelpers::sanitize( _buf, _frames );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LMMS_DEBUG
|
||||
|
||||
@@ -116,18 +116,10 @@ void FxChannel::unmuteForSolo()
|
||||
|
||||
|
||||
|
||||
void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
void FxChannel::doProcessing()
|
||||
{
|
||||
const fpp_t fpp = engine::mixer()->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;
|
||||
const bool exporting = engine::getSong()->isExporting();
|
||||
|
||||
if( m_muted == false )
|
||||
{
|
||||
@@ -150,21 +142,25 @@ void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
if( ! volBuf && ! sendBuf ) // neither volume nor send has sample-exact data...
|
||||
{
|
||||
const float v = sender->m_volumeModel.value() * sendModel->value();
|
||||
MixHelpers::addMultiplied( _buf, ch_buf, v, fpp );
|
||||
if( exporting ) { MixHelpers::addSanitizedMultiplied( m_buffer, ch_buf, v, fpp ); }
|
||||
else { MixHelpers::addMultiplied( m_buffer, ch_buf, v, fpp ); }
|
||||
}
|
||||
else if( volBuf && sendBuf ) // both volume and send have sample-exact data
|
||||
{
|
||||
MixHelpers::addMultipliedByBuffers( _buf, ch_buf, volBuf, sendBuf, fpp );
|
||||
if( exporting ) { MixHelpers::addSanitizedMultipliedByBuffers( m_buffer, ch_buf, volBuf, sendBuf, fpp ); }
|
||||
else { MixHelpers::addMultipliedByBuffers( m_buffer, ch_buf, volBuf, sendBuf, fpp ); }
|
||||
}
|
||||
else if( volBuf ) // volume has sample-exact data but send does not
|
||||
{
|
||||
const float v = sendModel->value();
|
||||
MixHelpers::addMultipliedByBuffer( _buf, ch_buf, v, volBuf, fpp );
|
||||
if( exporting ) { MixHelpers::addSanitizedMultipliedByBuffer( m_buffer, ch_buf, v, volBuf, fpp ); }
|
||||
else { MixHelpers::addMultipliedByBuffer( m_buffer, ch_buf, v, volBuf, fpp ); }
|
||||
}
|
||||
else // vice versa
|
||||
{
|
||||
const float v = sender->m_volumeModel.value();
|
||||
MixHelpers::addMultipliedByBuffer( _buf, ch_buf, v, sendBuf, fpp );
|
||||
if( exporting ) { MixHelpers::addSanitizedMultipliedByBuffer( m_buffer, ch_buf, v, sendBuf, fpp ); }
|
||||
else { MixHelpers::addMultipliedByBuffer( m_buffer, ch_buf, v, sendBuf, fpp ); }
|
||||
}
|
||||
m_hasInput = true;
|
||||
}
|
||||
@@ -179,10 +175,10 @@ void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
m_fxChain.startRunning();
|
||||
}
|
||||
|
||||
m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp, m_hasInput );
|
||||
m_stillRunning = m_fxChain.processAudioBuffer( m_buffer, fpp, m_hasInput );
|
||||
|
||||
m_peakLeft = qMax( m_peakLeft, engine::mixer()->peakValueLeft( _buf, fpp ) * v );
|
||||
m_peakRight = qMax( m_peakRight, engine::mixer()->peakValueRight( _buf, fpp ) * v );
|
||||
m_peakLeft = qMax( m_peakLeft, engine::mixer()->peakValueLeft( m_buffer, fpp ) * v );
|
||||
m_peakRight = qMax( m_peakRight, engine::mixer()->peakValueRight( m_buffer, fpp ) * v );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -260,8 +260,10 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
|
||||
|
||||
// create sub-note-play-handle, only note is
|
||||
// different
|
||||
new NotePlayHandle( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
|
||||
_n, -1, NotePlayHandle::OriginNoteStacking );
|
||||
engine::mixer()->addPlayHandle(
|
||||
NotePlayHandleManager::acquire( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
|
||||
_n, -1, NotePlayHandle::OriginNoteStacking )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,7 +379,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
cnphv.first()->totalFramesPlayed() :
|
||||
_n->totalFramesPlayed() ) + arp_frames - 1;
|
||||
// used for loop
|
||||
f_cnt_t frames_processed = 0;
|
||||
f_cnt_t frames_processed = ( m_arpModeModel.value() != FreeMode ) ? cnphv.first()->noteOffset() : _n->noteOffset();
|
||||
|
||||
while( frames_processed < engine::mixer()->framesPerPeriod() )
|
||||
{
|
||||
@@ -471,12 +473,14 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
|
||||
// create sub-note-play-handle, only ptr to note is different
|
||||
// and is_arp_note=true
|
||||
new NotePlayHandle( _n->instrumentTrack(),
|
||||
( ( m_arpModeModel.value() != FreeMode ) ? cnphv.first()->offset() : _n->offset() ) + frames_processed,
|
||||
engine::mixer()->addPlayHandle(
|
||||
NotePlayHandleManager::acquire( _n->instrumentTrack(),
|
||||
frames_processed,
|
||||
gated_frames,
|
||||
note( MidiTime( 0 ), MidiTime( 0 ), sub_note_key, (volume_t) qRound( _n->getVolume() * vol_level ),
|
||||
_n->getPanning(), _n->detuning() ),
|
||||
_n, -1, NotePlayHandle::OriginArpeggio );
|
||||
_n, -1, NotePlayHandle::OriginArpeggio )
|
||||
);
|
||||
|
||||
// update counters
|
||||
frames_processed += arp_frames;
|
||||
|
||||
35
src/core/InstrumentPlayHandle.cpp
Normal file
35
src/core/InstrumentPlayHandle.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* InstrumentPlayHandle.cpp - play-handle for driving an instrument
|
||||
*
|
||||
* Copyright (c) 2005-2014 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 "InstrumentPlayHandle.h"
|
||||
#include "InstrumentTrack.h"
|
||||
|
||||
InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) :
|
||||
PlayHandle( TypeInstrumentPlayHandle ),
|
||||
m_instrument( instrument ),
|
||||
m_instrumentTrack( instrumentTrack )
|
||||
{
|
||||
setAudioPort( instrumentTrack->audioPort() );
|
||||
}
|
||||
@@ -130,6 +130,25 @@ LADSPA_Data LadspaControl::value()
|
||||
}
|
||||
|
||||
|
||||
ValueBuffer * LadspaControl::valueBuffer()
|
||||
{
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case INTEGER:
|
||||
return NULL;
|
||||
case FLOATING:
|
||||
return m_knobModel.valueBuffer();
|
||||
case TIME:
|
||||
return m_tempoSyncKnobModel.valueBuffer();
|
||||
default:
|
||||
qWarning( "LadspaControl::valueBuffer(): BAD BAD BAD\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LadspaControl::setValue( LADSPA_Data _value )
|
||||
|
||||
218
src/core/MemoryManager.cpp
Normal file
218
src/core/MemoryManager.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* MemoryManager.cpp - A lightweight, generic memory manager for LMMS
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki
|
||||
* Copyright (c) 2007-2014 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 "MemoryManager.h"
|
||||
#include <QtGlobal>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
MemoryPoolVector MemoryManager::s_memoryPools;
|
||||
QReadWriteLock MemoryManager::s_poolMutex;
|
||||
PointerInfoMap MemoryManager::s_pointerInfo;
|
||||
QMutex MemoryManager::s_pointerMutex;
|
||||
|
||||
|
||||
bool MemoryManager::init()
|
||||
{
|
||||
s_memoryPools.reserve( 64 );
|
||||
s_pointerInfo.reserve( 4096 );
|
||||
// construct first MemoryPool and allocate memory
|
||||
MemoryPool m ( MM_INITIAL_CHUNKS );
|
||||
m.m_pool = MemoryHelper::alignedMalloc( MM_INITIAL_CHUNKS * MM_CHUNK_SIZE );
|
||||
s_memoryPools.append( m );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void * MemoryManager::alloc( size_t size )
|
||||
{
|
||||
int requiredChunks = size / MM_CHUNK_SIZE + ( size % MM_CHUNK_SIZE > 0 ? 1 : 0 );
|
||||
|
||||
MemoryPool * mp = NULL;
|
||||
void * ptr = NULL;
|
||||
|
||||
MemoryPoolVector::iterator it = s_memoryPools.begin();
|
||||
|
||||
s_poolMutex.lockForRead();
|
||||
while( it != s_memoryPools.end() && !ptr )
|
||||
{
|
||||
ptr = ( *it ).getChunks( requiredChunks );
|
||||
if( ptr )
|
||||
{
|
||||
mp = &( *it );
|
||||
}
|
||||
++it;
|
||||
}
|
||||
s_poolMutex.unlock();
|
||||
|
||||
if( ptr )
|
||||
{
|
||||
s_pointerMutex.lock();
|
||||
PtrInfo p;
|
||||
p.chunks = requiredChunks;
|
||||
p.memPool = mp;
|
||||
s_pointerInfo[ptr] = p;
|
||||
s_pointerMutex.unlock();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// can't find enough chunks in existing pools, so
|
||||
// create a new pool that is guaranteed to have enough chunks
|
||||
int moreChunks = qMax( requiredChunks, MM_INCREMENT_CHUNKS );
|
||||
int i = MemoryManager::extend( moreChunks );
|
||||
|
||||
mp = &s_memoryPools[i];
|
||||
ptr = s_memoryPools[i].getChunks( requiredChunks );
|
||||
if( ptr )
|
||||
{
|
||||
s_pointerMutex.lock();
|
||||
PtrInfo p;
|
||||
p.chunks = requiredChunks;
|
||||
p.memPool = mp;
|
||||
s_pointerInfo[ptr] = p;
|
||||
s_pointerMutex.unlock();
|
||||
return ptr;
|
||||
}
|
||||
// still no luck? something is horribly wrong
|
||||
qFatal( "MemoryManager.cpp: Couldn't allocate memory: %d chunks asked", requiredChunks );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void MemoryManager::free( void * ptr )
|
||||
{
|
||||
if( ptr == NULL )
|
||||
{
|
||||
qDebug( "MemoryManager: Null pointer deallocation attempted" );
|
||||
return; // let's not try to deallocate null pointers, ok?
|
||||
}
|
||||
|
||||
// fetch info on the ptr and remove
|
||||
s_pointerMutex.lock();
|
||||
if( ! s_pointerInfo.contains( ptr ) ) // if we have no info on ptr, fail loudly
|
||||
{
|
||||
qFatal( "MemoryManager: Couldn't find pointer info for pointer: %p", ptr );
|
||||
}
|
||||
PtrInfo p = s_pointerInfo[ptr];
|
||||
s_pointerInfo.remove( ptr );
|
||||
s_pointerMutex.unlock();
|
||||
|
||||
p.memPool->releaseChunks( ptr, p.chunks );
|
||||
}
|
||||
|
||||
|
||||
int MemoryManager::extend( int chunks )
|
||||
{
|
||||
MemoryPool m ( chunks );
|
||||
m.m_pool = MemoryHelper::alignedMalloc( chunks * MM_CHUNK_SIZE );
|
||||
|
||||
s_poolMutex.lockForWrite();
|
||||
s_memoryPools.append( m );
|
||||
int i = s_memoryPools.size() - 1;
|
||||
s_poolMutex.unlock();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
void MemoryManager::cleanup()
|
||||
{
|
||||
for( MemoryPoolVector::iterator it = s_memoryPools.begin(); it != s_memoryPools.end(); ++it )
|
||||
{
|
||||
MemoryHelper::alignedFree( ( *it ).m_pool );
|
||||
MemoryHelper::alignedFree( ( *it ).m_free );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void * MemoryPool::getChunks( int chunksNeeded )
|
||||
{
|
||||
if( chunksNeeded > m_chunks ) // not enough chunks in this pool?
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_mutex.lock();
|
||||
|
||||
// now find out if we have a long enough sequence of chunks in this pool
|
||||
char last = 0;
|
||||
intptr_t n = 0;
|
||||
intptr_t index = -1;
|
||||
bool found = false;
|
||||
|
||||
for( int i = 0; i < m_chunks; ++i )
|
||||
{
|
||||
if( m_free[i] )
|
||||
{
|
||||
if( !last )
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
|
||||
++n;
|
||||
if( n >= chunksNeeded )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
n = 0;
|
||||
}
|
||||
|
||||
last = m_free[i];
|
||||
}
|
||||
|
||||
if( found ) // if enough chunks found, return pointer to chunks
|
||||
{
|
||||
// set chunk flags to false so we know the chunks are in use
|
||||
for( intptr_t i = 0; i < chunksNeeded; ++i )
|
||||
{
|
||||
m_free[ index + i ] = 0;
|
||||
}
|
||||
m_mutex.unlock();
|
||||
return (char*)m_pool + ( index * MM_CHUNK_SIZE );
|
||||
}
|
||||
m_mutex.unlock();
|
||||
return NULL; // out of stock, come again tomorrow!
|
||||
}
|
||||
|
||||
|
||||
void MemoryPool::releaseChunks( void * ptr, int chunks )
|
||||
{
|
||||
m_mutex.lock();
|
||||
|
||||
intptr_t start = ( (intptr_t)ptr - (intptr_t)m_pool ) / MM_CHUNK_SIZE;
|
||||
if( start < 0 )
|
||||
{
|
||||
qFatal( "MemoryManager: error at releaseChunks() - corrupt pointer info?" );
|
||||
}
|
||||
|
||||
memset( &m_free[ start ], 1, chunks );
|
||||
|
||||
m_mutex.unlock();
|
||||
}
|
||||
@@ -69,6 +69,25 @@ bool isSilent( const sampleFrame* src, int frames )
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Function for sanitizing a buffer of infs/nans - returns true if those are found */
|
||||
bool sanitize( sampleFrame * src, int frames )
|
||||
{
|
||||
bool found = false;
|
||||
for( int f = 0; f < frames; ++f )
|
||||
{
|
||||
for( int c = 0; c < 2; ++c )
|
||||
{
|
||||
if( isinff( src[f][c] ) || isnanf( src[f][c] ) )
|
||||
{
|
||||
src[f][c] = 0.0f;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
struct AddOp
|
||||
{
|
||||
void operator()( sampleFrame& dst, const sampleFrame& src ) const
|
||||
@@ -124,11 +143,34 @@ void addMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuff
|
||||
|
||||
}
|
||||
|
||||
void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames )
|
||||
{
|
||||
for( int f = 0; f < frames; ++f )
|
||||
{
|
||||
dst[f][0] += ( isinff( src[f][0] ) || isnanf( src[f][0] ) ) ? 0.0f : src[f][0] * coeffSrc * coeffSrcBuf->values()[f];
|
||||
dst[f][1] += ( isinff( src[f][1] ) || isnanf( src[f][1] ) ) ? 0.0f : src[f][1] * coeffSrc * coeffSrcBuf->values()[f];
|
||||
}
|
||||
}
|
||||
|
||||
void addSanitizedMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames )
|
||||
{
|
||||
for( int f = 0; f < frames; ++f )
|
||||
{
|
||||
dst[f][0] += ( isinff( src[f][0] ) || isnanf( src[f][0] ) )
|
||||
? 0.0f
|
||||
: src[f][0] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
|
||||
dst[f][1] += ( isinff( src[f][1] ) || isnanf( src[f][1] ) )
|
||||
? 0.0f
|
||||
: src[f][1] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct AddSanitizedMultipliedOp
|
||||
{
|
||||
AddSanitizedMultipliedOp( float coeff ) : m_coeff( coeff ) { }
|
||||
|
||||
|
||||
void operator()( sampleFrame& dst, const sampleFrame& src ) const
|
||||
{
|
||||
dst[0] += ( isinff( src[0] ) || isnanf( src[0] ) ) ? 0.0f : src[0] * m_coeff;
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#include "MidiDummy.h"
|
||||
|
||||
#include "MemoryHelper.h"
|
||||
|
||||
#include "BufferManager.h"
|
||||
|
||||
|
||||
|
||||
@@ -118,6 +118,9 @@ Mixer::Mixer() :
|
||||
m_fifo = new fifo( 1 );
|
||||
}
|
||||
|
||||
// now that framesPerPeriod is fixed initialize global BufferManager
|
||||
BufferManager::init( m_framesPerPeriod );
|
||||
|
||||
m_workingBuf = (sampleFrame*) MemoryHelper::alignedMalloc( m_framesPerPeriod *
|
||||
sizeof( sampleFrame ) );
|
||||
for( int i = 0; i < 3; i++ )
|
||||
@@ -355,7 +358,12 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
|
||||
if( it != m_playHandles.end() )
|
||||
{
|
||||
delete *it;
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
}
|
||||
else delete *it;
|
||||
m_playHandles.erase( it );
|
||||
}
|
||||
|
||||
@@ -406,7 +414,12 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
}
|
||||
if( ( *it )->isFinished() )
|
||||
{
|
||||
delete *it;
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
}
|
||||
else delete *it;
|
||||
it = m_playHandles.erase( it );
|
||||
}
|
||||
else
|
||||
@@ -416,7 +429,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
}
|
||||
unlockPlayHandleRemoval();
|
||||
|
||||
|
||||
// STAGE 2: process effects of all instrument- and sampletracks
|
||||
MixerWorkerThread::fillJobQueue<QVector<AudioPort *> >( m_audioPorts );
|
||||
MixerWorkerThread::startAndWaitForJobs();
|
||||
@@ -434,6 +446,9 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
EnvelopeAndLfoParameters::instances()->trigger();
|
||||
Controller::triggerFrameCounter();
|
||||
AutomatableModel::incrementPeriodCounter();
|
||||
|
||||
// refresh buffer pool
|
||||
BufferManager::refresh();
|
||||
|
||||
m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod );
|
||||
|
||||
@@ -464,45 +479,6 @@ void Mixer::clear()
|
||||
|
||||
|
||||
|
||||
void Mixer::bufferToPort( const sampleFrame * buf,
|
||||
const fpp_t frames,
|
||||
stereoVolumeVector vv,
|
||||
AudioPort * port )
|
||||
{
|
||||
const int loop1_frame = qMin<int>( frames, m_framesPerPeriod );
|
||||
|
||||
port->lockFirstBuffer();
|
||||
MixHelpers::addMultipliedStereo( port->firstBuffer(), // dst
|
||||
buf, // src
|
||||
vv.vol[0], vv.vol[1], // coeff left/right
|
||||
loop1_frame ); // frame count
|
||||
port->unlockFirstBuffer();
|
||||
|
||||
if( frames > m_framesPerPeriod )
|
||||
{
|
||||
port->lockSecondBuffer();
|
||||
|
||||
const fpp_t framesLeft = qMin<int>( frames - m_framesPerPeriod, m_framesPerPeriod );
|
||||
|
||||
MixHelpers::addMultipliedStereo( port->secondBuffer(), // dst
|
||||
buf + m_framesPerPeriod, // src
|
||||
vv.vol[0], vv.vol[1], // coeff left/right
|
||||
framesLeft ); // frame count
|
||||
|
||||
// we used both buffers so set flags
|
||||
port->m_bufferUsage = AudioPort::BothBuffers;
|
||||
port->unlockSecondBuffer();
|
||||
}
|
||||
else if( port->m_bufferUsage == AudioPort::NoUsage )
|
||||
{
|
||||
// only first buffer touched
|
||||
port->m_bufferUsage = AudioPort::FirstBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Mixer::clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames,
|
||||
const f_cnt_t _offset )
|
||||
{
|
||||
@@ -652,6 +628,25 @@ void Mixer::removeAudioPort( AudioPort * _port )
|
||||
}
|
||||
|
||||
|
||||
bool Mixer::addPlayHandle( PlayHandle* handle )
|
||||
{
|
||||
if( criticalXRuns() == false )
|
||||
{
|
||||
m_playHandleMutex.lock();
|
||||
m_newPlayHandles.append( handle );
|
||||
handle->audioPort()->addPlayHandle( handle );
|
||||
m_playHandleMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
if( handle->type() == PlayHandle::TypeNotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*)handle );
|
||||
}
|
||||
else delete handle;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
@@ -662,13 +657,18 @@ void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
_ph->affinity() == QThread::currentThread() )
|
||||
{
|
||||
lockPlayHandleRemoval();
|
||||
_ph->audioPort()->removePlayHandle( _ph );
|
||||
PlayHandleList::Iterator it =
|
||||
qFind( m_playHandles.begin(),
|
||||
m_playHandles.end(), _ph );
|
||||
if( it != m_playHandles.end() )
|
||||
{
|
||||
m_playHandles.erase( it );
|
||||
delete _ph;
|
||||
if( _ph->type() == PlayHandle::TypeNotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) _ph );
|
||||
}
|
||||
else delete _ph;
|
||||
}
|
||||
unlockPlayHandleRemoval();
|
||||
}
|
||||
@@ -689,7 +689,12 @@ void Mixer::removePlayHandles( track * _track, bool removeIPHs )
|
||||
{
|
||||
if( ( *it )->isFromTrack( _track ) && ( removeIPHs || ( *it )->type() != PlayHandle::TypeInstrumentPlayHandle ) )
|
||||
{
|
||||
delete *it;
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
}
|
||||
else delete *it;
|
||||
it = m_playHandles.erase( it );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -56,7 +56,7 @@ void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job )
|
||||
|
||||
|
||||
|
||||
void MixerWorkerThread::JobQueue::run( sampleFrame * _buffer )
|
||||
void MixerWorkerThread::JobQueue::run()
|
||||
{
|
||||
bool processedJob = true;
|
||||
while( processedJob && (int) m_itemsDone < (int) m_queueSize )
|
||||
@@ -67,7 +67,7 @@ void MixerWorkerThread::JobQueue::run( sampleFrame * _buffer )
|
||||
ThreadableJob * job = m_items[i].fetchAndStoreOrdered( NULL );
|
||||
if( job )
|
||||
{
|
||||
job->process( _buffer );
|
||||
job->process();
|
||||
processedJob = true;
|
||||
m_itemsDone.fetchAndAddOrdered( 1 );
|
||||
}
|
||||
@@ -98,7 +98,6 @@ void MixerWorkerThread::JobQueue::wait()
|
||||
|
||||
MixerWorkerThread::MixerWorkerThread( Mixer* mixer ) :
|
||||
QThread( mixer ),
|
||||
m_workingBuf( new sampleFrame[mixer->framesPerPeriod()] ),
|
||||
m_quit( false )
|
||||
{
|
||||
// initialize global static data
|
||||
@@ -120,8 +119,6 @@ MixerWorkerThread::MixerWorkerThread( Mixer* mixer ) :
|
||||
|
||||
MixerWorkerThread::~MixerWorkerThread()
|
||||
{
|
||||
delete[] m_workingBuf;
|
||||
|
||||
workerThreads.removeAll( this );
|
||||
}
|
||||
|
||||
@@ -143,7 +140,7 @@ void MixerWorkerThread::startAndWaitForJobs()
|
||||
// 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.run();
|
||||
globalJobQueue.wait();
|
||||
}
|
||||
|
||||
@@ -166,7 +163,7 @@ void MixerWorkerThread::run()
|
||||
{
|
||||
m.lock();
|
||||
queueReadyWaitCond->wait( &m );
|
||||
globalJobQueue.run( m_workingBuf );
|
||||
globalJobQueue.run();
|
||||
m.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,8 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
m_baseDetuning( NULL ),
|
||||
m_songGlobalParentOffset( 0 ),
|
||||
m_midiChannel( midiEventChannel >= 0 ? midiEventChannel : instrumentTrack->midiPort()->realOutputChannel() ),
|
||||
m_origin( origin )
|
||||
m_origin( origin ),
|
||||
m_frequencyNeedsUpdate( false )
|
||||
{
|
||||
lock();
|
||||
if( hasParent() == false )
|
||||
@@ -92,18 +93,43 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
parent->m_hadChildren = true;
|
||||
|
||||
m_bbTrack = parent->m_bbTrack;
|
||||
|
||||
parent->setUsesBuffer( false );
|
||||
}
|
||||
|
||||
updateFrequency();
|
||||
|
||||
setFrames( _frames );
|
||||
|
||||
// inform attached components about new MIDI note (used for recording in Piano Roll)
|
||||
if( m_origin == OriginMidiInput )
|
||||
{
|
||||
m_instrumentTrack->midiNoteOn( *this );
|
||||
}
|
||||
|
||||
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
|
||||
{
|
||||
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
|
||||
|
||||
// send MidiNoteOn event
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ),
|
||||
MidiTime::fromFrames( offset(), engine::framesPerTick() ),
|
||||
offset() );
|
||||
}
|
||||
|
||||
if( m_instrumentTrack->instrument()->flags() & Instrument::IsSingleStreamed )
|
||||
{
|
||||
setUsesBuffer( false );
|
||||
}
|
||||
|
||||
setAudioPort( instrumentTrack->audioPort() );
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
NotePlayHandle::~NotePlayHandle()
|
||||
void NotePlayHandle::done()
|
||||
{
|
||||
lock();
|
||||
noteOff( 0 );
|
||||
@@ -128,13 +154,12 @@ NotePlayHandle::~NotePlayHandle()
|
||||
m_instrumentTrack->m_notes[key()] = NULL;
|
||||
}
|
||||
|
||||
foreach( NotePlayHandle * n, m_subNotes )
|
||||
{
|
||||
delete n;
|
||||
}
|
||||
m_subNotes.clear();
|
||||
|
||||
delete m_filter;
|
||||
|
||||
if( buffer() ) releaseBuffer();
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
@@ -189,25 +214,9 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
}
|
||||
|
||||
lock();
|
||||
|
||||
if( m_totalFramesPlayed == 0 )
|
||||
if( m_frequencyNeedsUpdate )
|
||||
{
|
||||
// inform attached components about new MIDI note (used for recording in Piano Roll)
|
||||
if( m_origin == OriginMidiInput )
|
||||
{
|
||||
m_instrumentTrack->midiNoteOn( *this );
|
||||
}
|
||||
|
||||
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
|
||||
{
|
||||
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
|
||||
|
||||
// send MidiNoteOn event
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ),
|
||||
MidiTime::fromFrames( offset(), engine::framesPerTick() ),
|
||||
offset() );
|
||||
}
|
||||
updateFrequency();
|
||||
}
|
||||
|
||||
// number of frames that can be played this period
|
||||
@@ -294,14 +303,15 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
}
|
||||
|
||||
// play sub-notes (e.g. chords)
|
||||
foreach( NotePlayHandle * n, m_subNotes )
|
||||
// handled by mixer now
|
||||
/* foreach( NotePlayHandle * n, m_subNotes )
|
||||
{
|
||||
n->play( _working_buffer );
|
||||
if( n->isFinished() )
|
||||
{
|
||||
delete n;
|
||||
NotePlayHandleManager::release( n );
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// update internal data
|
||||
m_totalFramesPlayed += framesThisPeriod;
|
||||
@@ -362,7 +372,9 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
|
||||
// first note-off all sub-notes
|
||||
foreach( NotePlayHandle * n, m_subNotes )
|
||||
{
|
||||
n->lock();
|
||||
n->noteOff( _s );
|
||||
n->unlock();
|
||||
}
|
||||
|
||||
// then set some variables indicating release-state
|
||||
@@ -545,3 +557,72 @@ void NotePlayHandle::resize( const bpm_t _new_tempo )
|
||||
}
|
||||
|
||||
|
||||
NotePlayHandle ** NotePlayHandleManager::s_available;
|
||||
QReadWriteLock NotePlayHandleManager::s_mutex;
|
||||
QAtomicInt NotePlayHandleManager::s_availableIndex;
|
||||
int NotePlayHandleManager::s_size;
|
||||
|
||||
|
||||
void NotePlayHandleManager::init()
|
||||
{
|
||||
s_available = MM_ALLOC( NotePlayHandle*, INITIAL_NPH_CACHE );
|
||||
|
||||
NotePlayHandle * n = MM_ALLOC( NotePlayHandle, INITIAL_NPH_CACHE );
|
||||
|
||||
for( int i=0; i < INITIAL_NPH_CACHE; ++i )
|
||||
{
|
||||
s_available[ i ] = n;
|
||||
++n;
|
||||
}
|
||||
s_availableIndex = INITIAL_NPH_CACHE - 1;
|
||||
s_size = INITIAL_NPH_CACHE;
|
||||
}
|
||||
|
||||
|
||||
NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrack,
|
||||
const f_cnt_t offset,
|
||||
const f_cnt_t frames,
|
||||
const note& noteToPlay,
|
||||
NotePlayHandle* parent,
|
||||
int midiEventChannel,
|
||||
NotePlayHandle::Origin origin )
|
||||
{
|
||||
if( s_availableIndex < 0 )
|
||||
{
|
||||
s_mutex.lockForWrite();
|
||||
if( s_availableIndex < 0 ) extend( NPH_CACHE_INCREMENT );
|
||||
s_mutex.unlock();
|
||||
}
|
||||
s_mutex.lockForRead();
|
||||
NotePlayHandle * nph = s_available[ s_availableIndex.fetchAndAddOrdered( -1 ) ];
|
||||
s_mutex.unlock();
|
||||
|
||||
new( (void*)nph ) NotePlayHandle( instrumentTrack, offset, frames, noteToPlay, parent, midiEventChannel, origin );
|
||||
return nph;
|
||||
}
|
||||
|
||||
|
||||
void NotePlayHandleManager::release( NotePlayHandle * nph )
|
||||
{
|
||||
nph->done();
|
||||
s_mutex.lockForRead();
|
||||
s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = nph;
|
||||
s_mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
void NotePlayHandleManager::extend( int c )
|
||||
{
|
||||
s_size += c;
|
||||
NotePlayHandle ** tmp = MM_ALLOC( NotePlayHandle*, s_size );
|
||||
MM_FREE( s_available );
|
||||
s_available = tmp;
|
||||
|
||||
NotePlayHandle * n = MM_ALLOC( NotePlayHandle, c );
|
||||
|
||||
for( int i=0; i < c; ++i )
|
||||
{
|
||||
s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = n;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@ PeakController::PeakController( Model * _parent,
|
||||
connect( m_peakEffect, SIGNAL( destroyed( ) ),
|
||||
this, SLOT( handleDestroyedEffect( ) ) );
|
||||
}
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateCoeffs() ) );
|
||||
connect( m_peakEffect->attackModel(), SIGNAL( dataChanged() ), this, SLOT( updateCoeffs() ) );
|
||||
connect( m_peakEffect->decayModel(), SIGNAL( dataChanged() ), this, SLOT( updateCoeffs() ) );
|
||||
m_coeffNeedsUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +84,14 @@ PeakController::~PeakController()
|
||||
|
||||
void PeakController::updateValueBuffer()
|
||||
{
|
||||
if( m_coeffNeedsUpdate )
|
||||
{
|
||||
const float ratio = 44100.0f / engine::mixer()->processingSampleRate();
|
||||
m_attackCoeff = 1.0f - powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->attackModel()->value() ) * ratio );
|
||||
m_decayCoeff = 1.0f - powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->decayModel()->value() ) * ratio );
|
||||
m_coeffNeedsUpdate = false;
|
||||
}
|
||||
|
||||
if( m_peakEffect )
|
||||
{
|
||||
float targetSample = m_peakEffect->lastSample();
|
||||
@@ -87,24 +99,17 @@ void PeakController::updateValueBuffer()
|
||||
{
|
||||
const f_cnt_t frames = engine::mixer()->framesPerPeriod();
|
||||
float * values = m_valueBuffer.values();
|
||||
|
||||
const float ratio = ( 44100.0 / 256.0 ) / engine::mixer()->processingSampleRate();
|
||||
const float v = m_currentSample >= targetSample
|
||||
? m_peakEffect->decayModel()->value()
|
||||
: m_peakEffect->attackModel()->value();
|
||||
const float diff = ( targetSample - m_currentSample ) * ratio;
|
||||
const float a = ( 1.0f - sqrt_neg( sqrt_neg( v ) ) ) * diff;
|
||||
const bool att = m_currentSample < targetSample;
|
||||
|
||||
for( f_cnt_t f = 0; f < frames; ++f )
|
||||
{
|
||||
if( att ) // going up...
|
||||
const float diff = ( targetSample - m_currentSample );
|
||||
if( m_currentSample < targetSample ) // going up...
|
||||
{
|
||||
m_currentSample = qMin( targetSample, m_currentSample + a ); // qmin prevents overshoot
|
||||
m_currentSample += diff * m_attackCoeff;
|
||||
}
|
||||
else
|
||||
else if( m_currentSample > targetSample ) // going down
|
||||
{
|
||||
m_currentSample = qMax( targetSample, m_currentSample + a ); // qmax prevents overshoot
|
||||
m_currentSample += diff * m_decayCoeff;
|
||||
}
|
||||
values[f] = m_currentSample;
|
||||
}
|
||||
@@ -122,6 +127,12 @@ void PeakController::updateValueBuffer()
|
||||
}
|
||||
|
||||
|
||||
void PeakController::updateCoeffs()
|
||||
{
|
||||
m_coeffNeedsUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
void PeakController::handleDestroyedEffect( )
|
||||
{
|
||||
// possible race condition...
|
||||
|
||||
62
src/core/PlayHandle.cpp
Normal file
62
src/core/PlayHandle.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* PlayHandle.cpp - base class PlayHandle - core of rendering engine
|
||||
*
|
||||
* Copyright (c) 2004-2014 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 "PlayHandle.h"
|
||||
#include "BufferManager.h"
|
||||
|
||||
|
||||
PlayHandle::PlayHandle( const Type type, f_cnt_t offset ) :
|
||||
m_type( type ),
|
||||
m_offset( offset ),
|
||||
m_affinity( QThread::currentThread() ),
|
||||
m_playHandleBuffer( NULL ),
|
||||
m_usesBuffer( true )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PlayHandle::~PlayHandle()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void PlayHandle::doProcessing()
|
||||
{
|
||||
if( m_usesBuffer )
|
||||
{
|
||||
if( ! m_playHandleBuffer ) m_playHandleBuffer = BufferManager::acquire();
|
||||
play( m_playHandleBuffer );
|
||||
}
|
||||
else
|
||||
{
|
||||
play( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PlayHandle::releaseBuffer()
|
||||
{
|
||||
if( m_playHandleBuffer ) BufferManager::release( m_playHandleBuffer );
|
||||
m_playHandleBuffer = NULL;
|
||||
}
|
||||
@@ -160,11 +160,12 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
|
||||
midiPort()->setMode( MidiPort::Disabled );
|
||||
|
||||
// create note-play-handle for it
|
||||
m_previewNote = new NotePlayHandle(
|
||||
m_previewNote = NotePlayHandleManager::acquire(
|
||||
s_previewTC->previewInstrumentTrack(), 0,
|
||||
typeInfo<f_cnt_t>::max() / 2,
|
||||
note( 0, 0, DefaultKey, 100 ) );
|
||||
|
||||
setAudioPort( s_previewTC->previewInstrumentTrack()->audioPort() );
|
||||
|
||||
s_previewTC->setPreviewNote( m_previewNote );
|
||||
|
||||
@@ -184,7 +185,7 @@ PresetPreviewPlayHandle::~PresetPreviewPlayHandle()
|
||||
// then set according state
|
||||
s_previewTC->setPreviewNote( NULL );
|
||||
}
|
||||
delete m_previewNote;
|
||||
NotePlayHandleManager::release( m_previewNote );
|
||||
s_previewTC->unlockData();
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "templates.h"
|
||||
|
||||
#include "FileDialog.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
|
||||
SampleBuffer::SampleBuffer( const QString & _audio_file,
|
||||
@@ -83,6 +84,7 @@ SampleBuffer::SampleBuffer( const QString & _audio_file,
|
||||
{
|
||||
loadFromBase64( _audio_file );
|
||||
}
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) );
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -106,10 +108,11 @@ SampleBuffer::SampleBuffer( const sampleFrame * _data, const f_cnt_t _frames ) :
|
||||
{
|
||||
if( _frames > 0 )
|
||||
{
|
||||
m_origData = new sampleFrame[_frames];
|
||||
m_origData = MM_ALLOC( sampleFrame, _frames );
|
||||
memcpy( m_origData, _data, _frames * BYTES_PER_FRAME );
|
||||
m_origFrames = _frames;
|
||||
}
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) );
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -133,10 +136,11 @@ SampleBuffer::SampleBuffer( const f_cnt_t _frames ) :
|
||||
{
|
||||
if( _frames > 0 )
|
||||
{
|
||||
m_origData = new sampleFrame[_frames];
|
||||
m_origData = MM_ALLOC( sampleFrame, _frames );
|
||||
memset( m_origData, 0, _frames * BYTES_PER_FRAME );
|
||||
m_origFrames = _frames;
|
||||
}
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) );
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -145,13 +149,16 @@ SampleBuffer::SampleBuffer( const f_cnt_t _frames ) :
|
||||
|
||||
SampleBuffer::~SampleBuffer()
|
||||
{
|
||||
delete[] m_origData;
|
||||
delete[] m_data;
|
||||
MM_FREE( m_origData );
|
||||
MM_FREE( m_data );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SampleBuffer::sampleRateChanged()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void SampleBuffer::update( bool _keep_settings )
|
||||
@@ -159,15 +166,15 @@ void SampleBuffer::update( bool _keep_settings )
|
||||
const bool lock = ( m_data != NULL );
|
||||
if( lock )
|
||||
{
|
||||
engine::mixer()->lock();
|
||||
delete[] m_data;
|
||||
m_varLock.lockForWrite();
|
||||
MM_FREE( m_data );
|
||||
}
|
||||
|
||||
if( m_audioFile.isEmpty() && m_origData != NULL && m_origFrames > 0 )
|
||||
{
|
||||
// TODO: reverse- and amplification-property is not covered
|
||||
// by following code...
|
||||
m_data = new sampleFrame[m_origFrames];
|
||||
m_data = MM_ALLOC( sampleFrame, m_origFrames );
|
||||
memcpy( m_data, m_origData, m_origFrames * BYTES_PER_FRAME );
|
||||
if( _keep_settings == false )
|
||||
{
|
||||
@@ -232,7 +239,7 @@ void SampleBuffer::update( bool _keep_settings )
|
||||
{
|
||||
// sample couldn't be decoded, create buffer containing
|
||||
// one sample-frame
|
||||
m_data = new sampleFrame[1];
|
||||
m_data = MM_ALLOC( sampleFrame, 1 );
|
||||
memset( m_data, 0, sizeof( *m_data ) );
|
||||
m_frames = 1;
|
||||
m_loopStartFrame = m_startFrame = 0;
|
||||
@@ -252,7 +259,7 @@ void SampleBuffer::update( bool _keep_settings )
|
||||
{
|
||||
// neither an audio-file nor a buffer to copy from, so create
|
||||
// buffer containing one sample-frame
|
||||
m_data = new sampleFrame[1];
|
||||
m_data = MM_ALLOC( sampleFrame, 1 );
|
||||
memset( m_data, 0, sizeof( *m_data ) );
|
||||
m_frames = 1;
|
||||
m_loopStartFrame = m_startFrame = 0;
|
||||
@@ -261,7 +268,7 @@ void SampleBuffer::update( bool _keep_settings )
|
||||
|
||||
if( lock )
|
||||
{
|
||||
engine::mixer()->unlock();
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
emit sampleUpdated();
|
||||
@@ -273,7 +280,7 @@ void SampleBuffer::convertIntToFloat ( int_sample_t * & _ibuf, f_cnt_t _frames,
|
||||
// following code transforms int-samples into
|
||||
// float-samples and does amplifying & reversing
|
||||
const float fac = 1 / OUTPUT_SAMPLE_MULTIPLIER;
|
||||
m_data = new sampleFrame[_frames];
|
||||
m_data = MM_ALLOC( sampleFrame, _frames );
|
||||
const int ch = ( _channels > 1 ) ? 1 : 0;
|
||||
|
||||
// if reversing is on, we also reverse when
|
||||
@@ -313,7 +320,7 @@ void SampleBuffer::directFloatWrite ( sample_t * & _fbuf, f_cnt_t _frames, int _
|
||||
|
||||
{
|
||||
|
||||
m_data = new sampleFrame[_frames];
|
||||
m_data = MM_ALLOC( sampleFrame, _frames );
|
||||
const int ch = ( _channels > 1 ) ? 1 : 0;
|
||||
|
||||
// if reversing is on, we also reverse when
|
||||
@@ -356,9 +363,9 @@ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr,
|
||||
{
|
||||
SampleBuffer * resampled = resample( this, _src_sr,
|
||||
engine::mixer()->baseSampleRate() );
|
||||
delete[] m_data;
|
||||
MM_FREE( m_data );
|
||||
m_frames = resampled->frames();
|
||||
m_data = new sampleFrame[m_frames];
|
||||
m_data = MM_ALLOC( sampleFrame, m_frames );
|
||||
memcpy( m_data, resampled->data(), m_frames *
|
||||
sizeof( sampleFrame ) );
|
||||
delete resampled;
|
||||
@@ -597,7 +604,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
const float _freq,
|
||||
const LoopMode _loopmode )
|
||||
{
|
||||
QMutexLocker ml( &m_varLock );
|
||||
m_varLock.lockForRead();
|
||||
|
||||
f_cnt_t startFrame = m_startFrame;
|
||||
f_cnt_t endFrame = m_endFrame;
|
||||
@@ -606,6 +613,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
|
||||
if( endFrame == 0 || _frames == 0 )
|
||||
{
|
||||
m_varLock.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -622,6 +630,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
|
||||
if( total_frames_for_current_pitch == 0 )
|
||||
{
|
||||
m_varLock.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -638,6 +647,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
{
|
||||
if( play_frame >= endFrame )
|
||||
{
|
||||
m_varLock.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -654,6 +664,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
play_frame = getPingPongIndex( play_frame, loopStartFrame, loopEndFrame );
|
||||
}
|
||||
|
||||
f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) + MARGIN[ _state->interpolationMode() ];
|
||||
|
||||
sampleFrame * tmp = NULL;
|
||||
|
||||
@@ -662,7 +673,6 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
{
|
||||
SRC_DATA src_data;
|
||||
// Generate output
|
||||
f_cnt_t fragment_size = (f_cnt_t)( _frames * freq_factor ) + MARGIN[ _state->interpolationMode() ];
|
||||
src_data.data_in =
|
||||
getSampleFragment( play_frame, fragment_size, _loopmode, &tmp, &is_backwards,
|
||||
loopStartFrame, loopEndFrame, endFrame )[0];
|
||||
@@ -752,7 +762,10 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
}
|
||||
}
|
||||
|
||||
if( tmp != NULL ) delete[] tmp;
|
||||
if( tmp != NULL )
|
||||
{
|
||||
MM_FREE( tmp );
|
||||
}
|
||||
|
||||
_state->setBackwards( is_backwards );
|
||||
_state->setFrameIndex( play_frame );
|
||||
@@ -763,8 +776,8 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state,
|
||||
_ab[i][1] *= m_amplification;
|
||||
}
|
||||
|
||||
m_varLock.unlock();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -794,7 +807,7 @@ sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t _index,
|
||||
return m_data + _index;
|
||||
}
|
||||
|
||||
*_tmp = new sampleFrame[_frames];
|
||||
*_tmp = MM_ALLOC( sampleFrame, _frames );
|
||||
|
||||
if( _loopmode == LoopOff )
|
||||
{
|
||||
@@ -1336,15 +1349,15 @@ void SampleBuffer::loadFromBase64( const QString & _data )
|
||||
printf("%d\n", (int) orig_data.size() );
|
||||
|
||||
m_origFrames = orig_data.size() / sizeof( sampleFrame );
|
||||
delete[] m_origData;
|
||||
m_origData = new sampleFrame[m_origFrames];
|
||||
MM_FREE( m_origData );
|
||||
m_origData = MM_ALLOC( sampleFrame, m_origFrames );
|
||||
memcpy( m_origData, orig_data.data(), orig_data.size() );
|
||||
|
||||
#else /* LMMS_HAVE_FLAC_STREAM_DECODER_H */
|
||||
|
||||
m_origFrames = dsize / sizeof( sampleFrame );
|
||||
delete[] m_origData;
|
||||
m_origData = new sampleFrame[m_origFrames];
|
||||
MM_FREE( m_origData );
|
||||
m_origData = MM_ALLOC( sampleFrame, m_origFrames );
|
||||
memcpy( m_origData, dst, dsize );
|
||||
|
||||
#endif
|
||||
@@ -1360,7 +1373,7 @@ void SampleBuffer::loadFromBase64( const QString & _data )
|
||||
|
||||
void SampleBuffer::setStartFrame( const f_cnt_t _s )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_varLock.lockForWrite();
|
||||
m_startFrame = _s;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
@@ -1370,7 +1383,7 @@ void SampleBuffer::setStartFrame( const f_cnt_t _s )
|
||||
|
||||
void SampleBuffer::setEndFrame( const f_cnt_t _e )
|
||||
{
|
||||
m_varLock.lock();
|
||||
m_varLock.lockForWrite();
|
||||
m_endFrame = _e;
|
||||
m_varLock.unlock();
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ SamplePlayHandle::SamplePlayHandle( const QString& sampleFile ) :
|
||||
m_sampleBuffer( new SampleBuffer( sampleFile ) ),
|
||||
m_doneMayReturnTrue( true ),
|
||||
m_frame( 0 ),
|
||||
m_audioPort( new AudioPort( "SamplePlayHandle", false ) ),
|
||||
m_ownAudioPort( true ),
|
||||
m_defaultVolumeModel( DefaultVolume, MinVolume, MaxVolume, 1 ),
|
||||
m_volumeModel( &m_defaultVolumeModel ),
|
||||
m_track( NULL ),
|
||||
m_bbTrack( NULL )
|
||||
{
|
||||
setAudioPort( new AudioPort( "SamplePlayHandle", false ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -55,13 +55,13 @@ SamplePlayHandle::SamplePlayHandle( SampleBuffer* sampleBuffer ) :
|
||||
m_sampleBuffer( sharedObject::ref( sampleBuffer ) ),
|
||||
m_doneMayReturnTrue( true ),
|
||||
m_frame( 0 ),
|
||||
m_audioPort( new AudioPort( "SamplePlayHandle", false ) ),
|
||||
m_ownAudioPort( true ),
|
||||
m_defaultVolumeModel( DefaultVolume, MinVolume, MaxVolume, 1 ),
|
||||
m_volumeModel( &m_defaultVolumeModel ),
|
||||
m_track( NULL ),
|
||||
m_bbTrack( NULL )
|
||||
{
|
||||
setAudioPort( new AudioPort( "SamplePlayHandle", false ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -72,13 +72,13 @@ SamplePlayHandle::SamplePlayHandle( SampleTCO* tco ) :
|
||||
m_sampleBuffer( sharedObject::ref( tco->sampleBuffer() ) ),
|
||||
m_doneMayReturnTrue( true ),
|
||||
m_frame( 0 ),
|
||||
m_audioPort( ( (SampleTrack *)tco->getTrack() )->audioPort() ),
|
||||
m_ownAudioPort( false ),
|
||||
m_defaultVolumeModel( DefaultVolume, MinVolume, MaxVolume, 1 ),
|
||||
m_volumeModel( &m_defaultVolumeModel ),
|
||||
m_track( tco->getTrack() ),
|
||||
m_bbTrack( NULL )
|
||||
{
|
||||
setAudioPort( ( (SampleTrack *)tco->getTrack() )->audioPort() );
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ SamplePlayHandle::~SamplePlayHandle()
|
||||
sharedObject::unref( m_sampleBuffer );
|
||||
if( m_ownAudioPort )
|
||||
{
|
||||
delete m_audioPort;
|
||||
delete audioPort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +119,14 @@ void SamplePlayHandle::play( sampleFrame * buffer )
|
||||
if( !( m_track && m_track->isMuted() )
|
||||
&& !( m_bbTrack && m_bbTrack->isMuted() ) )
|
||||
{
|
||||
stereoVolumeVector v =
|
||||
/* stereoVolumeVector v =
|
||||
{ { m_volumeModel->value() / DefaultVolume,
|
||||
m_volumeModel->value() / DefaultVolume } };
|
||||
m_sampleBuffer->play( workingBuffer, &m_state, frames,
|
||||
BaseFreq );
|
||||
engine::mixer()->bufferToPort( buffer, fpp,
|
||||
v, m_audioPort );
|
||||
m_volumeModel->value() / DefaultVolume } };*/
|
||||
if( ! m_sampleBuffer->play( workingBuffer, &m_state, frames,
|
||||
BaseFreq ) )
|
||||
{
|
||||
memset( workingBuffer, 0, frames * sizeof( sampleFrame ) );
|
||||
}
|
||||
}
|
||||
|
||||
m_frame += frames;
|
||||
|
||||
@@ -27,20 +27,23 @@
|
||||
#include "EffectChain.h"
|
||||
#include "FxMixer.h"
|
||||
#include "engine.h"
|
||||
#include "MixHelpers.h"
|
||||
#include "BufferManager.h"
|
||||
#include "ValueBuffer.h"
|
||||
#include "panning.h"
|
||||
|
||||
|
||||
AudioPort::AudioPort( const QString & _name, bool _has_effect_chain ) :
|
||||
m_bufferUsage( NoUsage ),
|
||||
m_firstBuffer( new sampleFrame[engine::mixer()->framesPerPeriod()] ),
|
||||
m_secondBuffer( new sampleFrame[
|
||||
engine::mixer()->framesPerPeriod()] ),
|
||||
AudioPort::AudioPort( const QString & _name, bool _has_effect_chain,
|
||||
FloatModel * volumeModel, FloatModel * panningModel ) :
|
||||
m_bufferUsage( false ),
|
||||
m_portBuffer( NULL ),
|
||||
m_extOutputEnabled( false ),
|
||||
m_nextFxChannel( 0 ),
|
||||
m_name( "unnamed port" ),
|
||||
m_effects( _has_effect_chain ? new EffectChain( NULL ) : NULL )
|
||||
m_effects( _has_effect_chain ? new EffectChain( NULL ) : NULL ),
|
||||
m_volumeModel( volumeModel ),
|
||||
m_panningModel( panningModel )
|
||||
{
|
||||
engine::mixer()->clearAudioBuffer( m_firstBuffer, engine::mixer()->framesPerPeriod() );
|
||||
engine::mixer()->clearAudioBuffer( m_secondBuffer, engine::mixer()->framesPerPeriod() );
|
||||
engine::mixer()->addAudioPort( this );
|
||||
setExtOutputEnabled( true );
|
||||
}
|
||||
@@ -52,31 +55,12 @@ AudioPort::~AudioPort()
|
||||
{
|
||||
setExtOutputEnabled( false );
|
||||
engine::mixer()->removeAudioPort( this );
|
||||
delete[] m_firstBuffer;
|
||||
delete[] m_secondBuffer;
|
||||
delete m_effects;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioPort::nextPeriod()
|
||||
{
|
||||
m_firstBufferLock.lock();
|
||||
engine::mixer()->clearAudioBuffer( m_firstBuffer, engine::mixer()->framesPerPeriod() );
|
||||
qSwap( m_firstBuffer, m_secondBuffer );
|
||||
|
||||
// this is how we decrease state of buffer-usage ;-)
|
||||
m_bufferUsage = ( m_bufferUsage != NoUsage ) ?
|
||||
( ( m_bufferUsage == FirstBuffer ) ?
|
||||
NoUsage : FirstBuffer ) : NoUsage;
|
||||
|
||||
m_firstBufferLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioPort::setExtOutputEnabled( bool _enabled )
|
||||
{
|
||||
if( _enabled != m_extOutputEnabled )
|
||||
@@ -109,23 +93,152 @@ bool AudioPort::processEffects()
|
||||
{
|
||||
if( m_effects )
|
||||
{
|
||||
lockFirstBuffer();
|
||||
bool hasInputNoise = m_bufferUsage != NoUsage;
|
||||
bool more = m_effects->processAudioBuffer( m_firstBuffer, engine::mixer()->framesPerPeriod(), hasInputNoise );
|
||||
unlockFirstBuffer();
|
||||
bool more = m_effects->processAudioBuffer( m_portBuffer, engine::mixer()->framesPerPeriod(), m_bufferUsage );
|
||||
return more;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void AudioPort::doProcessing( sampleFrame * )
|
||||
void AudioPort::doProcessing()
|
||||
{
|
||||
const bool me = processEffects();
|
||||
if( me || m_bufferUsage != NoUsage )
|
||||
const fpp_t fpp = engine::mixer()->framesPerPeriod();
|
||||
|
||||
m_portBuffer = BufferManager::acquire(); // get buffer for processing
|
||||
|
||||
engine::mixer()->clearAudioBuffer( m_portBuffer, fpp ); // clear the audioport buffer so we can use it
|
||||
|
||||
//qDebug( "Playhandles: %d", m_playHandles.size() );
|
||||
foreach( PlayHandle * ph, m_playHandles ) // now we mix all playhandle buffers into the audioport buffer
|
||||
{
|
||||
engine::fxMixer()->mixToChannel( firstBuffer(), nextFxChannel() );
|
||||
nextPeriod();
|
||||
if( ph->buffer() )
|
||||
{
|
||||
if( ph->usesBuffer() )
|
||||
{
|
||||
m_bufferUsage = true;
|
||||
MixHelpers::add( m_portBuffer, ph->buffer(), fpp );
|
||||
}
|
||||
ph->releaseBuffer(); // gets rid of playhandle's buffer and sets
|
||||
// pointer to null, so if it doesn't get re-acquired we know to skip it next time
|
||||
}
|
||||
}
|
||||
|
||||
if( m_bufferUsage )
|
||||
{
|
||||
// handle volume and panning
|
||||
// has both vol and pan models
|
||||
if( m_volumeModel && m_panningModel )
|
||||
{
|
||||
ValueBuffer * volBuf = m_volumeModel->valueBuffer();
|
||||
ValueBuffer * panBuf = m_panningModel->valueBuffer();
|
||||
|
||||
// both vol and pan have s.ex.data:
|
||||
if( volBuf && panBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
float p = panBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v;
|
||||
m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v;
|
||||
}
|
||||
}
|
||||
|
||||
// only vol has s.ex.data:
|
||||
else if( volBuf )
|
||||
{
|
||||
float p = m_panningModel->value() * 0.01f;
|
||||
float l = ( p <= 0 ? 1.0f : 1.0f - p );
|
||||
float r = ( p >= 0 ? 1.0f : 1.0f + p );
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= v * l;
|
||||
m_portBuffer[f][1] *= v * r;
|
||||
}
|
||||
}
|
||||
|
||||
// only pan has s.ex.data:
|
||||
else if( panBuf )
|
||||
{
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float p = panBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v;
|
||||
m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v;
|
||||
}
|
||||
}
|
||||
|
||||
// neither has s.ex.data:
|
||||
else
|
||||
{
|
||||
float p = m_panningModel->value() * 0.01f;
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v;
|
||||
m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// has vol model only
|
||||
else if( m_volumeModel )
|
||||
{
|
||||
ValueBuffer * volBuf = m_volumeModel->valueBuffer();
|
||||
|
||||
if( volBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= v;
|
||||
m_portBuffer[f][1] *= v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
m_portBuffer[f][0] *= v;
|
||||
m_portBuffer[f][1] *= v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// as of now there's no situation where we only have panning model but no volume model
|
||||
// if we have neither, we don't have to do anything here - just pass the audio as is
|
||||
|
||||
// handle effects
|
||||
const bool me = processEffects();
|
||||
if( me || m_bufferUsage )
|
||||
{
|
||||
engine::fxMixer()->mixToChannel( m_portBuffer, m_nextFxChannel ); // send output to fx mixer
|
||||
// TODO: improve the flow here - convert to pull model
|
||||
m_bufferUsage = false;
|
||||
}
|
||||
|
||||
BufferManager::release( m_portBuffer ); // release buffer, we don't need it anymore
|
||||
}
|
||||
|
||||
|
||||
void AudioPort::addPlayHandle( PlayHandle * handle )
|
||||
{
|
||||
m_playHandleLock.lock();
|
||||
m_playHandles.append( handle );
|
||||
m_playHandleLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
void AudioPort::removePlayHandle( PlayHandle * handle )
|
||||
{
|
||||
m_playHandleLock.lock();
|
||||
PlayHandleList::Iterator it = qFind( m_playHandles.begin(), m_playHandles.end(), handle );
|
||||
if( it != m_playHandles.end() )
|
||||
{
|
||||
m_playHandles.erase( it );
|
||||
}
|
||||
m_playHandleLock.unlock();
|
||||
}
|
||||
|
||||
@@ -64,7 +64,9 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "MemoryManager.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "embed.h"
|
||||
#include "engine.h"
|
||||
#include "LmmsStyle.h"
|
||||
@@ -97,6 +99,10 @@ inline void loadTranslation( const QString & _tname,
|
||||
|
||||
int main( int argc, char * * argv )
|
||||
{
|
||||
// initialize memory managers
|
||||
MemoryManager::init();
|
||||
NotePlayHandleManager::init();
|
||||
|
||||
// intialize RNG
|
||||
srand( getpid() + time( 0 ) );
|
||||
|
||||
@@ -432,7 +438,7 @@ int main( int argc, char * * argv )
|
||||
|
||||
// init central engine which handles all components of LMMS
|
||||
engine::init();
|
||||
|
||||
|
||||
splashScreen.hide();
|
||||
|
||||
// re-intialize RNG - shared libraries might have srand() or
|
||||
@@ -499,6 +505,7 @@ int main( int argc, char * * argv )
|
||||
{
|
||||
// we're going to render our song
|
||||
engine::init( false );
|
||||
|
||||
printf( "loading project...\n" );
|
||||
engine::getSong()->loadProject( file_to_load );
|
||||
printf( "done\n" );
|
||||
@@ -529,6 +536,10 @@ int main( int argc, char * * argv )
|
||||
|
||||
const int ret = app->exec();
|
||||
delete app;
|
||||
|
||||
// cleanup memory managers
|
||||
MemoryManager::cleanup();
|
||||
|
||||
return( ret );
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
#include "tooltip.h"
|
||||
#include "track_label_button.h"
|
||||
#include "ValueBuffer.h"
|
||||
#include "volume.h"
|
||||
|
||||
|
||||
const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack",
|
||||
@@ -94,7 +95,6 @@ const int INSTRUMENT_WINDOW_CACHE_SIZE = 8;
|
||||
InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
track( track::InstrumentTrack, tc ),
|
||||
MidiEventProcessor(),
|
||||
m_audioPort( tr( "unnamed_track" ) ),
|
||||
m_midiPort( tr( "unnamed_track" ), engine::mixer()->midiClient(),
|
||||
this, this ),
|
||||
m_notes(),
|
||||
@@ -104,6 +104,7 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
tr( "Base note" ) ),
|
||||
m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr( "Volume" ) ),
|
||||
m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ),
|
||||
m_audioPort( tr( "unnamed_track" ), true, &m_volumeModel, &m_panningModel ),
|
||||
m_pitchModel( 0, MinPitchDefault, MaxPitchDefault, 1, this, tr( "Pitch" ) ),
|
||||
m_pitchRangeModel( 1, 1, 24, this, tr( "Pitch range" ) ),
|
||||
m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ),
|
||||
@@ -148,7 +149,7 @@ InstrumentTrack::~InstrumentTrack()
|
||||
silenceAllNotes( true );
|
||||
|
||||
// now we're save deleting the instrument
|
||||
delete m_instrument;
|
||||
if( m_instrument ) delete m_instrument;
|
||||
}
|
||||
|
||||
|
||||
@@ -188,10 +189,10 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames,
|
||||
|
||||
// get volume knob data
|
||||
static const float DefaultVolumeRatio = 1.0f / DefaultVolume;
|
||||
ValueBuffer * volBuf = m_volumeModel.valueBuffer();
|
||||
/*ValueBuffer * volBuf = m_volumeModel.valueBuffer();
|
||||
float v_scale = volBuf
|
||||
? 1.0f
|
||||
: getVolume() * DefaultVolumeRatio;
|
||||
: getVolume() * DefaultVolumeRatio;*/
|
||||
|
||||
// instruments using instrument-play-handles will call this method
|
||||
// without any knowledge about notes, so they pass NULL for n, which
|
||||
@@ -200,44 +201,19 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames,
|
||||
{
|
||||
const f_cnt_t offset = n->noteOffset();
|
||||
m_soundShaping.processAudioBuffer( buf + offset, frames - offset, n );
|
||||
v_scale *= ( (float) n->getVolume() * DefaultVolumeRatio );
|
||||
const float vol = ( (float) n->getVolume() * DefaultVolumeRatio );
|
||||
const panning_t pan = qBound( PanningLeft, n->getPanning(), PanningRight );
|
||||
stereoVolumeVector vv = panningToVolumeVector( pan, vol );
|
||||
for( f_cnt_t f = offset; f < frames; ++f )
|
||||
{
|
||||
for( int c = 0; c < 2; ++c )
|
||||
{
|
||||
buf[f][c] *= vv.vol[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_audioPort.setNextFxChannel( m_effectChannelModel.value() );
|
||||
|
||||
// get panning knob data
|
||||
ValueBuffer * panBuf = m_panningModel.valueBuffer();
|
||||
int panning = panBuf
|
||||
? 0
|
||||
: m_panningModel.value();
|
||||
|
||||
if( n )
|
||||
{
|
||||
panning += n->getPanning();
|
||||
panning = tLimit<int>( panning, PanningLeft, PanningRight );
|
||||
}
|
||||
|
||||
// apply sample-exact volume/panning data
|
||||
if( volBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < frames; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
buf[f][0] *= v;
|
||||
buf[f][1] *= v;
|
||||
}
|
||||
}
|
||||
if( panBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < frames; ++f )
|
||||
{
|
||||
float p = panBuf->values()[ f ] * 0.01f;
|
||||
buf[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p );
|
||||
buf[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p );
|
||||
}
|
||||
}
|
||||
|
||||
engine::mixer()->bufferToPort( buf, frames, panningToVolumeVector( panning, v_scale ), &m_audioPort );
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +254,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti
|
||||
m_notesMutex.lock();
|
||||
if( m_notes[event.key()] == NULL )
|
||||
{
|
||||
nph = new NotePlayHandle( this, offset,
|
||||
nph = NotePlayHandleManager::acquire( this, offset,
|
||||
typeInfo<f_cnt_t>::max() / 2,
|
||||
note( MidiTime(), MidiTime(), event.key(), event.volume( midiPort()->baseVelocity() ) ),
|
||||
NULL, event.channel(),
|
||||
@@ -538,9 +514,7 @@ void InstrumentTrack::updateBaseNote()
|
||||
for( NotePlayHandleList::Iterator it = m_processHandles.begin();
|
||||
it != m_processHandles.end(); ++it )
|
||||
{
|
||||
( *it )->lock();
|
||||
( *it )->updateFrequency();
|
||||
( *it )->unlock();
|
||||
( *it )->setFrequencyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,7 +644,7 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames,
|
||||
cur_note->length().frames(
|
||||
frames_per_tick );
|
||||
|
||||
NotePlayHandle* notePlayHandle = new NotePlayHandle( this, _offset, note_frames, *cur_note );
|
||||
NotePlayHandle* notePlayHandle = NotePlayHandleManager::acquire( this, _offset, note_frames, *cur_note );
|
||||
notePlayHandle->setBBTrack( bb_track );
|
||||
// are we playing global song?
|
||||
if( _tco_num < 0 )
|
||||
|
||||
@@ -403,9 +403,9 @@ void SampleTCOView::paintEvent( QPaintEvent * _pe )
|
||||
|
||||
SampleTrack::SampleTrack( TrackContainer* tc ) :
|
||||
track( track::SampleTrack, tc ),
|
||||
m_audioPort( tr( "Sample track" ) ),
|
||||
m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 1.0, this,
|
||||
tr( "Volume" ) )
|
||||
tr( "Volume" ) ),
|
||||
m_audioPort( tr( "Sample track" ), true, &m_volumeModel, NULL )
|
||||
{
|
||||
setName( tr( "Sample track" ) );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user