diff --git a/include/AudioPort.h b/include/AudioPort.h index 63a9e90eef7..0de61ca4d2d 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -22,55 +22,43 @@ * */ -#ifndef _AUDIO_PORT_H -#define _AUDIO_PORT_H +#ifndef AUDIO_PORT_H +#define AUDIO_PORT_H #include #include #include #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 @@ class AudioPort : public ThreadableJob 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 @@ class AudioPort : public ThreadableJob EffectChain * m_effects; + PlayHandleList m_playHandles; + QMutex m_playHandleLock; + + FloatModel * m_volumeModel; + FloatModel * m_panningModel; friend class Mixer; friend class MixerWorkerThread; diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 49e66c7623b..92f7df68e9c 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -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 AutoModelVector; diff --git a/include/BufferManager.h b/include/BufferManager.h new file mode 100644 index 00000000000..b437ae05ead --- /dev/null +++ b/include/BufferManager.h @@ -0,0 +1,59 @@ +/* + * BufferManager.h - A buffer caching/memory management system + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef BUFFER_MANAGER_H +#define BUFFER_MANAGER_H + +#include "MemoryManager.h" +#include "lmms_basics.h" +#include "engine.h" +#include "Mixer.h" +#include +#include + + +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 diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 716c1dc9d3a..567dbd1d9d6 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -34,7 +34,7 @@ #include #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() { diff --git a/include/DataFile.h b/include/DataFile.h index 651bd275e59..57db8905fd5 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -32,10 +32,11 @@ #include "export.h" #include "lmms_basics.h" - +#include "MemoryManager.h" class EXPORT DataFile : public QDomDocument { + MM_OPERATORS public: enum Types { diff --git a/include/DetuningHelper.h b/include/DetuningHelper.h index 7aa1d39d482..7f702ac4a71 100644 --- a/include/DetuningHelper.h +++ b/include/DetuningHelper.h @@ -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() diff --git a/include/Effect.h b/include/Effect.h index 8f261d4e42c..ded15c14e92 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -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, diff --git a/include/FxMixer.h b/include/FxMixer.h index be1d2486e0f..25c1a8c06de 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -76,7 +76,7 @@ class FxChannel : public ThreadableJob void processed(); private: - virtual void doProcessing( sampleFrame * _working_buffer ); + virtual void doProcessing(); }; diff --git a/include/Instrument.h b/include/Instrument.h index 904cf01fd26..7d367e5ce9d 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -43,6 +43,7 @@ class track; class EXPORT Instrument : public Plugin { + MM_OPERATORS public: enum Flag { diff --git a/include/InstrumentPlayHandle.h b/include/InstrumentPlayHandle.h index 331695a7e69..239e08d1f31 100644 --- a/include/InstrumentPlayHandle.h +++ b/include/InstrumentPlayHandle.h @@ -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 @@ class InstrumentPlayHandle : public PlayHandle private: Instrument* m_instrument; + InstrumentTrack * m_instrumentTrack; } ; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index e648a00e3f0..13a87b49c0d 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -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 @@ protected slots: FloatModel m_volumeModel; FloatModel m_panningModel; + + AudioPort m_audioPort; + FloatModel m_pitchModel; IntModel m_pitchRangeModel; IntModel m_effectChannelModel; diff --git a/include/LadspaControl.h b/include/LadspaControl.h index 4e50379f7f1..e9fc424de93 100644 --- a/include/LadspaControl.h +++ b/include/LadspaControl.h @@ -30,6 +30,7 @@ #include "AutomatableModel.h" #include "TempoSyncKnobModel.h" +#include "ValueBuffer.h" typedef struct PortDescription port_desc_t; @@ -44,6 +45,7 @@ class EXPORT LadspaControl : public Model, public JournallingObject ~LadspaControl(); LADSPA_Data value(); + ValueBuffer * valueBuffer(); void setValue( LADSPA_Data _value ); void setLink( bool _state ); diff --git a/include/MemoryManager.h b/include/MemoryManager.h new file mode 100644 index 00000000000..44ef58c6063 --- /dev/null +++ b/include/MemoryManager.h @@ -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 + * + * 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 +#include +#include +#include +#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 MemoryPoolVector; +typedef QHash 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 diff --git a/include/MidiEvent.h b/include/MidiEvent.h index 1ead0d56ff3..aa53bbeb3db 100644 --- a/include/MidiEvent.h +++ b/include/MidiEvent.h @@ -22,8 +22,8 @@ * */ -#ifndef _MIDI_EVENT_H -#define _MIDI_EVENT_H +#ifndef MIDI_EVENT_H +#define MIDI_EVENT_H #include #include "Midi.h" diff --git a/include/MidiEventProcessor.h b/include/MidiEventProcessor.h index 9da114577be..46eacea0bc4 100644 --- a/include/MidiEventProcessor.h +++ b/include/MidiEventProcessor.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() { diff --git a/include/MixHelpers.h b/include/MixHelpers.h index 9b953ffc7cb..4a2f510ee42 100644 --- a/include/MixHelpers.h +++ b/include/MixHelpers.h @@ -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 ); diff --git a/include/Mixer.h b/include/Mixer.h index 15c23e54e50..a17e7e7edb3 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -216,20 +216,7 @@ class EXPORT Mixer : public QObject // 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 @@ class EXPORT Mixer : public QObject } // 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 ); diff --git a/include/MixerWorkerThread.h b/include/MixerWorkerThread.h index 51da6a91e6a..5f5c18c844a 100644 --- a/include/MixerWorkerThread.h +++ b/include/MixerWorkerThread.h @@ -63,7 +63,7 @@ class MixerWorkerThread : public QThread void addJob( ThreadableJob * _job ); - void run( sampleFrame * _buffer ); + void run(); void wait(); private: @@ -115,7 +115,6 @@ class MixerWorkerThread : public QThread static QWaitCondition * queueReadyWaitCond; static QList workerThreads; - sampleFrame * m_workingBuf; volatile bool m_quit; } ; diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 6585efc1116..e11ddf45737 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -30,7 +30,9 @@ #include "note.h" #include "PlayHandle.h" #include "track.h" - +#include "MemoryManager.h" +#include +#include class InstrumentTrack; class NotePlayHandle; @@ -42,6 +44,7 @@ typedef QList ConstNotePlayHandleList; class EXPORT NotePlayHandle : public PlayHandle, public note { + MM_OPERATORS public: void * m_pluginData; basicFilters<> * m_filter; @@ -56,7 +59,7 @@ class EXPORT NotePlayHandle : public PlayHandle, public note OriginCount }; typedef Origins Origin; - + NotePlayHandle( InstrumentTrack* instrumentTrack, const f_cnt_t offset, const f_cnt_t frames, @@ -64,7 +67,13 @@ class EXPORT NotePlayHandle : public PlayHandle, public note 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 @@ class EXPORT NotePlayHandle : public PlayHandle, public note return m_frequency; } - void updateFrequency(); - /*! Returns frequency without pitch wheel influence */ float unpitchedFrequency() const { @@ -239,10 +246,15 @@ class EXPORT NotePlayHandle : public PlayHandle, public note return m_songGlobalParentOffset; } + void setFrequencyUpdate() + { + m_frequencyNeedsUpdate = true; + } private: class BaseDetuning { + MM_OPERATORS public: BaseDetuning( DetuningHelper* detuning ); @@ -262,6 +274,8 @@ class EXPORT NotePlayHandle : public PlayHandle, public note } ; + void updateFrequency(); + InstrumentTrack* m_instrumentTrack; // needed for calling // InstrumentTrack::playNote f_cnt_t m_frames; // total frames to play @@ -286,7 +300,7 @@ class EXPORT NotePlayHandle : public PlayHandle, public note 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 @@ class EXPORT NotePlayHandle : public PlayHandle, public note 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 diff --git a/include/Oscillator.h b/include/Oscillator.h index da3e8ceae6a..de7aee66b7b 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -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 { diff --git a/include/PeakController.h b/include/PeakController.h index 15f72f07f20..fc9ea92d8d4 100644 --- a/include/PeakController.h +++ b/include/PeakController.h @@ -61,7 +61,8 @@ class EXPORT PeakController : public Controller 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 @@ public slots: static int m_getCount; static int m_loadCount; static bool m_buggedFile; + + float m_attackCoeff; + float m_decayCoeff; + bool m_coeffNeedsUpdate; } ; diff --git a/include/PlayHandle.h b/include/PlayHandle.h index ba7609e429d..51130d21a68 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -33,7 +33,7 @@ #include "lmms_basics.h" class track; - +class AudioPort; class PlayHandle : public ThreadableJob { @@ -48,17 +48,18 @@ class PlayHandle : public ThreadableJob } ; 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 ); - virtual ~PlayHandle() + PlayHandle & operator = ( PlayHandle & p ) { + m_type = p.m_type; + m_offset = p.m_offset; + m_affinity = p.m_affinity; + return *this; } + virtual ~PlayHandle(); + virtual bool affinityMatters() const { return false; @@ -75,10 +76,7 @@ class PlayHandle : public ThreadableJob } // required for ThreadableJob - virtual void doProcessing( sampleFrame* buffer ) - { - play( buffer ); - } + virtual void doProcessing(); virtual bool requiresProcessing() const { @@ -98,7 +96,7 @@ class PlayHandle : public ThreadableJob 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 @@ class PlayHandle : public ThreadableJob 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; } ; diff --git a/include/Plugin.h b/include/Plugin.h index 4887ef0fbdc..f5c1ab04682 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -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 { diff --git a/include/PresetPreviewPlayHandle.h b/include/PresetPreviewPlayHandle.h index 673fa1f5fec..731a830cf84 100644 --- a/include/PresetPreviewPlayHandle.h +++ b/include/PresetPreviewPlayHandle.h @@ -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" diff --git a/include/RingBuffer.h b/include/RingBuffer.h index c668d1c33ce..ec4aa161988 100644 --- a/include/RingBuffer.h +++ b/include/RingBuffer.h @@ -31,10 +31,12 @@ #include #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 diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 1cb5864ca8a..ef97f84eebc 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -26,7 +26,7 @@ #ifndef SAMPLE_BUFFER_H #define SAMPLE_BUFFER_H -#include +#include #include #include @@ -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 @@ class EXPORT SampleBuffer : public QObject, public sharedObject }; class EXPORT handleState { + MM_OPERATORS public: handleState( bool _varying_pitch = false, int interpolation_mode = SRC_LINEAR ); virtual ~handleState(); @@ -148,21 +151,21 @@ class EXPORT SampleBuffer : public QObject, public sharedObject 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 @@ class EXPORT SampleBuffer : public QObject, public sharedObject 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 @@ public slots: 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; diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index 61bee7f5069..9721f04b4a2 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -82,7 +82,6 @@ class SamplePlayHandle : public PlayHandle f_cnt_t m_frame; SampleBuffer::handleState m_state; - AudioPort * m_audioPort; const bool m_ownAudioPort; FloatModel m_defaultVolumeModel; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index a3cf4571ccb..8673b74ec83 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -147,8 +147,8 @@ class SampleTrack : public track private: - AudioPort m_audioPort; FloatModel m_volumeModel; + AudioPort m_audioPort; friend class SampleTrackView; diff --git a/include/ThreadableJob.h b/include/ThreadableJob.h index c3da4c5474c..f116a9324b3 100644 --- a/include/ThreadableJob.h +++ b/include/ThreadableJob.h @@ -22,8 +22,8 @@ * */ -#ifndef _THREADABLE_JOB_H -#define _THREADABLE_JOB_H +#ifndef THREADABLE_JOB_H +#define THREADABLE_JOB_H #include @@ -67,11 +67,11 @@ class ThreadableJob 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 @@ class ThreadableJob protected: - virtual void doProcessing( sampleFrame* workingBuffer) = 0; + virtual void doProcessing() = 0; QAtomicInt m_state; diff --git a/include/ValueBuffer.h b/include/ValueBuffer.h index 7d97787216e..9c5e8bf456d 100644 --- a/include/ValueBuffer.h +++ b/include/ValueBuffer.h @@ -29,9 +29,11 @@ #include #include "interpolation.h" #include +#include "MemoryManager.h" class ValueBuffer { + MM_OPERATORS public: ValueBuffer() { diff --git a/include/basic_filters.h b/include/basic_filters.h index 76b782f6d84..0a8553a4754 100644 --- a/include/basic_filters.h +++ b/include/basic_filters.h @@ -43,6 +43,7 @@ #include "templates.h" #include "lmms_constants.h" #include "interpolation.h" +#include "MemoryManager.h" //#include //#include @@ -50,6 +51,7 @@ template class basicFilters { + MM_OPERATORS public: enum FilterTypes { diff --git a/include/engine.h b/include/engine.h index 0b21fe74ad6..7ca7e78f22c 100644 --- a/include/engine.h +++ b/include/engine.h @@ -23,10 +23,11 @@ */ -#ifndef _ENGINE_H -#define _ENGINE_H +#ifndef ENGINE_H +#define ENGINE_H #include "lmmsconfig.h" +#include "MemoryManager.h" #include diff --git a/include/track.h b/include/track.h index d57f01110a7..3c9812f6227 100644 --- a/include/track.h +++ b/include/track.h @@ -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 @@ private slots: class EXPORT track : public Model, public JournallingObject { Q_OBJECT + MM_OPERATORS mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel); mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel); public: diff --git a/plugins/LadspaEffect/LadspaEffect.cpp b/plugins/LadspaEffect/LadspaEffect.cpp index c2e4136538d..15b5c284ff1 100644 --- a/plugins/LadspaEffect/LadspaEffect.cpp +++ b/plugins/LadspaEffect/LadspaEffect.cpp @@ -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( - 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( + 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 ) ) diff --git a/plugins/LadspaEffect/LadspaEffect.h b/plugins/LadspaEffect/LadspaEffect.h index edc6fc840c3..fbfcaa8d695 100644 --- a/plugins/LadspaEffect/LadspaEffect.h +++ b/plugins/LadspaEffect/LadspaEffect.h @@ -76,6 +76,7 @@ private slots: sample_rate_t m_maxSampleRate; ladspa_key_t m_key; int m_portCount; + bool m_inPlaceBroken; const LADSPA_Descriptor * m_descriptor; QVector m_handles; diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp b/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp index f848944d46b..4f7fcd4d1e7 100644 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp +++ b/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.cpp @@ -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( combtuningL1 * m_sampleRatio )); + combR[0].setbuffer(bufcombR1,static_cast( combtuningR1 * m_sampleRatio )); + combL[1].setbuffer(bufcombL2,static_cast( combtuningL2 * m_sampleRatio )); + combR[1].setbuffer(bufcombR2,static_cast( combtuningR2 * m_sampleRatio )); + combL[2].setbuffer(bufcombL3,static_cast( combtuningL3 * m_sampleRatio )); + combR[2].setbuffer(bufcombR3,static_cast( combtuningR3 * m_sampleRatio )); + combL[3].setbuffer(bufcombL4,static_cast( combtuningL4 * m_sampleRatio )); + combR[3].setbuffer(bufcombR4,static_cast( combtuningR4 * m_sampleRatio )); + combL[4].setbuffer(bufcombL5,static_cast( combtuningL5 * m_sampleRatio )); + combR[4].setbuffer(bufcombR5,static_cast( combtuningR5 * m_sampleRatio )); + combL[5].setbuffer(bufcombL6,static_cast( combtuningL6 * m_sampleRatio )); + combR[5].setbuffer(bufcombR6,static_cast( combtuningR6 * m_sampleRatio )); + combL[6].setbuffer(bufcombL7,static_cast( combtuningL7 * m_sampleRatio )); + combR[6].setbuffer(bufcombR7,static_cast( combtuningR7 * m_sampleRatio )); + combL[7].setbuffer(bufcombL8,static_cast( combtuningL8 * m_sampleRatio )); + combR[7].setbuffer(bufcombR8,static_cast( combtuningR8 * m_sampleRatio )); + allpassL[0].setbuffer(bufallpassL1,static_cast( allpasstuningL1 * m_sampleRatio )); + allpassR[0].setbuffer(bufallpassR1,static_cast( allpasstuningR1 * m_sampleRatio )); + allpassL[1].setbuffer(bufallpassL2,static_cast( allpasstuningL2 * m_sampleRatio )); + allpassR[1].setbuffer(bufallpassR2,static_cast( allpasstuningR2 * m_sampleRatio )); + allpassL[2].setbuffer(bufallpassL3,static_cast( allpasstuningL3 * m_sampleRatio )); + allpassR[2].setbuffer(bufallpassR3,static_cast( allpasstuningR3 * m_sampleRatio )); + allpassL[3].setbuffer(bufallpassL4,static_cast( allpasstuningL4 * m_sampleRatio )); + allpassR[3].setbuffer(bufallpassR4,static_cast( allpasstuningR4 * m_sampleRatio )); // Set default values allpassL[0].setfeedback(0.5f); diff --git a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h b/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h index aec39dfeefd..9846eb40de3 100644 --- a/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h +++ b/plugins/LadspaEffect/cmt/src/freeverb/Components/revmodel.h @@ -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 @@ class revmodel 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 @@ class revmodel 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_ diff --git a/plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp b/plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp index 76057863010..418e4d91c47 100644 --- a/plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp +++ b/plugins/LadspaEffect/cmt/src/freeverb/freeverb.cpp @@ -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); - }; /*****************************************************************************/ diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index c2403e670a7..33c238aa3cf 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -179,6 +179,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, } else { + memset( _working_buffer, 0, ( frames + offset ) * sizeof( sampleFrame ) ); emit isPlaying( 0 ); } } diff --git a/plugins/bit_invader/bit_invader.h b/plugins/bit_invader/bit_invader.h index e7fff611fb8..d4eae0352bf 100644 --- a/plugins/bit_invader/bit_invader.h +++ b/plugins/bit_invader/bit_invader.h @@ -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, diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index b07e7119d26..167a476d2eb 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -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())); diff --git a/plugins/kicker/KickerOsc.h b/plugins/kicker/KickerOsc.h index a2b20aee834..bbe0dd584c2 100644 --- a/plugins/kicker/KickerOsc.h +++ b/plugins/kicker/KickerOsc.h @@ -31,11 +31,13 @@ #include "lmms_math.h" #include "interpolation.h" +#include "MemoryManager.h" template 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 ) : diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index b5e89499d3c..306273b1160 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -353,7 +353,7 @@ lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) : filterChanged(); - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrumentTrack ); engine::mixer()->addPlayHandle( iph ); } diff --git a/plugins/monstro/Monstro.h b/plugins/monstro/Monstro.h index fe9a1508c2c..8bbdcb83ecb 100644 --- a/plugins/monstro/Monstro.h +++ b/plugins/monstro/Monstro.h @@ -189,6 +189,7 @@ class MonstroView; class MonstroSynth { + MM_OPERATORS public: MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph ); virtual ~MonstroSynth(); diff --git a/plugins/nes/Nes.h b/plugins/nes/Nes.h index da321642a1c..337d31c6956 100644 --- a/plugins/nes/Nes.h +++ b/plugins/nes/Nes.h @@ -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(); diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index 786fe24836f..4f1e810ef22 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -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... diff --git a/plugins/organic/organic.h b/plugins/organic/organic.h index 92bd79cff0a..e4134976306 100644 --- a/plugins/organic/organic.h +++ b/plugins/organic/organic.h @@ -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 @@ public slots: struct oscPtr { + MM_OPERATORS Oscillator * oscLeft; Oscillator * oscRight; } ; @@ -180,6 +182,7 @@ class organicInstrumentView : public InstrumentView struct OscillatorKnobs { + MM_OPERATORS OscillatorKnobs( knob * h, knob * v, diff --git a/plugins/papu/Basic_Gb_Apu.h b/plugins/papu/Basic_Gb_Apu.h index ee01ed5788a..24b9dc7749d 100644 --- a/plugins/papu/Basic_Gb_Apu.h +++ b/plugins/papu/Basic_Gb_Apu.h @@ -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(); diff --git a/plugins/patman/patman.h b/plugins/patman/patman.h index 3568663d20c..9bf61db8eb1 100644 --- a/plugins/patman/patman.h +++ b/plugins/patman/patman.h @@ -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; diff --git a/plugins/peak_controller_effect/artwork.png b/plugins/peak_controller_effect/artwork.png index 31296ca9093..66fe2c95226 100644 Binary files a/plugins/peak_controller_effect/artwork.png and b/plugins/peak_controller_effect/artwork.png differ diff --git a/plugins/peak_controller_effect/peak_controller_effect.cpp b/plugins/peak_controller_effect/peak_controller_effect.cpp index 9903bb63506..136f22cbe43 100644 --- a/plugins/peak_controller_effect/peak_controller_effect.cpp +++ b/plugins/peak_controller_effect/peak_controller_effect.cpp @@ -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(); } diff --git a/plugins/peak_controller_effect/peak_controller_effect_control_dialog.cpp b/plugins/peak_controller_effect/peak_controller_effect_control_dialog.cpp index 7ea9448e811..359b23b9bf4 100644 --- a/plugins/peak_controller_effect/peak_controller_effect_control_dialog.cpp +++ b/plugins/peak_controller_effect/peak_controller_effect_control_dialog.cpp @@ -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 ); diff --git a/plugins/peak_controller_effect/peak_controller_effect_control_dialog.h b/plugins/peak_controller_effect/peak_controller_effect_control_dialog.h index b161a64c5e9..64e876e371b 100644 --- a/plugins/peak_controller_effect/peak_controller_effect_control_dialog.h +++ b/plugins/peak_controller_effect/peak_controller_effect_control_dialog.h @@ -48,6 +48,7 @@ class PeakControllerEffectControlDialog : public EffectControlDialog knob * m_amountKnob; knob * m_attackKnob; knob * m_decayKnob; + knob * m_tresholdKnob; ledCheckBox * m_muteLed; ledCheckBox * m_absLed; diff --git a/plugins/peak_controller_effect/peak_controller_effect_controls.cpp b/plugins/peak_controller_effect/peak_controller_effect_controls.cpp index 3fdeecc349c..78109366b97 100644 --- a/plugins/peak_controller_effect/peak_controller_effect_controls.cpp +++ b/plugins/peak_controller_effect/peak_controller_effect_controls.cpp @@ -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" ); } diff --git a/plugins/peak_controller_effect/peak_controller_effect_controls.h b/plugins/peak_controller_effect/peak_controller_effect_controls.h index 6f9cca92ec5..ad56fa99a9d 100644 --- a/plugins/peak_controller_effect/peak_controller_effect_controls.h +++ b/plugins/peak_controller_effect/peak_controller_effect_controls.h @@ -65,6 +65,7 @@ class PeakControllerEffectControls : public EffectControls FloatModel m_amountModel; FloatModel m_attackModel; FloatModel m_decayModel; + FloatModel m_tresholdModel; BoolModel m_muteModel; BoolModel m_absModel; FloatModel m_amountMultModel; diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index e6264cc260e..b6aefc12324 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -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() ); diff --git a/plugins/sf2_player/sf2_player.h b/plugins/sf2_player/sf2_player.h index 574bd81ed61..a7b9262b615 100644 --- a/plugins/sf2_player/sf2_player.h +++ b/plugins/sf2_player/sf2_player.h @@ -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 @@ public slots: // A soundfont in our font-map class sf2Font { + MM_OPERATORS public: sf2Font( fluid_sfont_t * f ) : fluidFont( f ), diff --git a/plugins/sfxr/sfxr.h b/plugins/sfxr/sfxr.h index c2da90caafc..d83c982838b 100644 --- a/plugins/sfxr/sfxr.h +++ b/plugins/sfxr/sfxr.h @@ -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(); diff --git a/plugins/sid/sid_instrument.h b/plugins/sid/sid_instrument.h index e6e73374322..45b6e7d4d2d 100644 --- a/plugins/sid/sid_instrument.h +++ b/plugins/sid/sid_instrument.h @@ -41,6 +41,7 @@ class pixmapButton; class voiceObject : public Model { Q_OBJECT + MM_OPERATORS public: enum WaveForm { SquareWave = 0, diff --git a/plugins/triple_oscillator/TripleOscillator.h b/plugins/triple_oscillator/TripleOscillator.h index a271c23e5cf..62247208067 100644 --- a/plugins/triple_oscillator/TripleOscillator.h +++ b/plugins/triple_oscillator/TripleOscillator.h @@ -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 @@ protected slots: struct oscPtr { + MM_OPERATORS Oscillator * oscLeft; Oscillator * oscRight; } ; @@ -150,6 +152,7 @@ class TripleOscillatorView : public InstrumentView struct OscillatorKnobs { + MM_OPERATORS OscillatorKnobs( knob * v, knob * p, knob * c, diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index f59a2906b8c..7a165d6acfe 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -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 ); } diff --git a/plugins/vibed/string_container.h b/plugins/vibed/string_container.h index 1e1864a0d4f..7d15e3dfe1e 100644 --- a/plugins/vibed/string_container.h +++ b/plugins/vibed/string_container.h @@ -27,11 +27,12 @@ #include #include "vibrating_string.h" - +#include "MemoryManager.h" class stringContainer { + MM_OPERATORS public: stringContainer(const float _pitch, const sample_rate_t _sample_rate, diff --git a/plugins/watsyn/Watsyn.h b/plugins/watsyn/Watsyn.h index c4fb63a5523..7b2a09bb43d 100644 --- a/plugins/watsyn/Watsyn.h +++ b/plugins/watsyn/Watsyn.h @@ -35,6 +35,7 @@ #include "NotePlayHandle.h" #include "pixmap_button.h" #include +#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, diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 00d3277dfd1..91b8f43b527 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -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() ), diff --git a/src/core/BufferManager.cpp b/src/core/BufferManager.cpp new file mode 100644 index 00000000000..b82b94e8f0f --- /dev/null +++ b/src/core/BufferManager.cpp @@ -0,0 +1,110 @@ +/* + * BufferManager.cpp - A buffer caching/memory management system + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "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(); + } +}*/ diff --git a/src/core/EffectChain.cpp b/src/core/EffectChain.cpp index 1d57de940d9..708cd9698e5 100644 --- a/src/core/EffectChain.cpp +++ b/src/core/EffectChain.cpp @@ -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 diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 9c52700d166..3437b431b34 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -116,18 +116,10 @@ void FxChannel::unmuteForSolo() -void FxChannel::doProcessing( sampleFrame * _buf ) +void FxChannel::doProcessing() { const fpp_t fpp = engine::mixer()->framesPerPeriod(); - - // ignore the passed _buf - // always use m_buffer - // this is just an auxilliary buffer if doProcessing() - // needs one for processing while running - // particularly important for playHandles, so Instruments - // can operate on this buffer the whole time - // 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 { diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 035f309ff4b..1afc1cb23db 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -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; diff --git a/src/core/InstrumentPlayHandle.cpp b/src/core/InstrumentPlayHandle.cpp new file mode 100644 index 00000000000..11a4fb5b8f9 --- /dev/null +++ b/src/core/InstrumentPlayHandle.cpp @@ -0,0 +1,35 @@ +/* + * InstrumentPlayHandle.cpp - play-handle for driving an instrument + * + * Copyright (c) 2005-2014 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "InstrumentPlayHandle.h" +#include "InstrumentTrack.h" + +InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) : + PlayHandle( TypeInstrumentPlayHandle ), + m_instrument( instrument ), + m_instrumentTrack( instrumentTrack ) +{ + setAudioPort( instrumentTrack->audioPort() ); +} diff --git a/src/core/LadspaControl.cpp b/src/core/LadspaControl.cpp index 42e74141e0b..2559fe3d9f1 100644 --- a/src/core/LadspaControl.cpp +++ b/src/core/LadspaControl.cpp @@ -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 ) diff --git a/src/core/MemoryManager.cpp b/src/core/MemoryManager.cpp new file mode 100644 index 00000000000..21cdf01bad8 --- /dev/null +++ b/src/core/MemoryManager.cpp @@ -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 + * + * 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 +#include + + +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(); +} diff --git a/src/core/MixHelpers.cpp b/src/core/MixHelpers.cpp index 154ec6024c4..a6e43e05ac9 100644 --- a/src/core/MixHelpers.cpp +++ b/src/core/MixHelpers.cpp @@ -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; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 1f19999e3fc..3bc852260b9 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -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 >( 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( 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( 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 diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index c82f9e03563..9cc65c4c4b2 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -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(); } } diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 5b428d26a6d..c4f3971a3b5 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -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 ); - unlock(); -} + + // 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; + } +} diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index 53798ea838a..87ea7a94843 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -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... diff --git a/src/core/PlayHandle.cpp b/src/core/PlayHandle.cpp new file mode 100644 index 00000000000..1e3666c0889 --- /dev/null +++ b/src/core/PlayHandle.cpp @@ -0,0 +1,62 @@ +/* + * PlayHandle.cpp - base class PlayHandle - core of rendering engine + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "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; +} diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index b518a1283e2..c6d5c713246 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -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::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(); } diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 2f69eb7ac9d..d0e0397fc8a 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -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(); } diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index 423f88784c3..88fdd1dda64 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -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; diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 26c1d7074b6..1fecc8d9e9a 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -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 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 + { + 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 != NoUsage ) + if( me || m_bufferUsage ) { - engine::fxMixer()->mixToChannel( firstBuffer(), nextFxChannel() ); - nextPeriod(); + 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(); +} diff --git a/src/core/main.cpp b/src/core/main.cpp index d33bf118ce0..dd95eaf64d3 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -64,7 +64,9 @@ #include #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 ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index dfc4e927824..60409043368 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -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 ); - } - - 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( 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 ) + 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 ) { - 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 ); + for( int c = 0; c < 2; ++c ) + { + buf[f][c] *= vv.vol[c]; + } } } - engine::mixer()->bufferToPort( buf, frames, panningToVolumeVector( panning, v_scale ), &m_audioPort ); + m_audioPort.setNextFxChannel( m_effectChannelModel.value() ); } @@ -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::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 ) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 0861428a2b2..f2a2dca5352 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -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" ) ); }