diff --git a/include/AudioPort.h b/include/AudioPort.h index 0971878d462..0de61ca4d2d 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -34,12 +34,14 @@ #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 * buffer() @@ -120,6 +122,9 @@ class AudioPort : public ThreadableJob PlayHandleList m_playHandles; QMutex m_playHandleLock; + + FloatModel * m_volumeModel; + FloatModel * m_panningModel; friend class Mixer; friend class MixerWorkerThread; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index eba606f00e2..13a87b49c0d 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -225,7 +225,6 @@ protected slots: private: - AudioPort m_audioPort; MidiPort m_midiPort; NotePlayHandle* m_notes[NumKeys]; @@ -244,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/SampleBuffer.h b/include/SampleBuffer.h index cb5d53c90cc..dd832281bb2 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 @@ -151,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; @@ -205,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(); } @@ -291,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/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/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/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 3612554f3f9..5d218ab06ba 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -160,7 +160,7 @@ void SampleBuffer::update( bool _keep_settings ) const bool lock = ( m_data != NULL ); if( lock ) { - engine::mixer()->lock(); + m_varLock.lockForWrite(); MM_FREE( m_data ); } @@ -262,7 +262,7 @@ void SampleBuffer::update( bool _keep_settings ) if( lock ) { - engine::mixer()->unlock(); + m_varLock.unlock(); } emit sampleUpdated(); @@ -598,7 +598,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; @@ -607,6 +607,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, if( endFrame == 0 || _frames == 0 ) { + m_varLock.unlock(); return false; } @@ -623,6 +624,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, if( total_frames_for_current_pitch == 0 ) { + m_varLock.unlock(); return false; } @@ -639,6 +641,7 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, { if( play_frame >= endFrame ) { + m_varLock.unlock(); return false; } @@ -655,6 +658,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; @@ -663,7 +667,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]; @@ -767,8 +770,8 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, _ab[i][1] *= m_amplification; } + m_varLock.unlock(); return true; - } @@ -1364,7 +1367,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(); } @@ -1374,7 +1377,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/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 93b447f0a80..85d99c91028 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -29,15 +29,20 @@ #include "engine.h" #include "MixHelpers.h" #include "BufferManager.h" +#include "ValueBuffer.h" +#include "panning.h" -AudioPort::AudioPort( const QString & _name, bool _has_effect_chain ) : +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()->addAudioPort( this ); setExtOutputEnabled( true ); @@ -120,10 +125,100 @@ void AudioPort::doProcessing() } } + if( m_bufferUsage ) + { + // handle volume and panning + // has both vol and pan models + if( m_volumeModel && m_panningModel ) + { + ValueBuffer * volBuf = m_volumeModel->valueBuffer(); + ValueBuffer * panBuf = m_panningModel->valueBuffer(); + + // both vol and pan have s.ex.data: + if( volBuf && panBuf ) + { + for( f_cnt_t f = 0; f < fpp; ++f ) + { + float v = volBuf->values()[ f ] * 0.01f; + float p = panBuf->values()[ f ] * 0.01f; + m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v; + m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v; + } + } + + // only vol has s.ex.data: + else if( volBuf ) + { + float p = m_panningModel->value() * 0.01f; + float l = ( p <= 0 ? 1.0f : 1.0f - p ); + float r = ( p >= 0 ? 1.0f : 1.0f + p ); + for( f_cnt_t f = 0; f < fpp; ++f ) + { + float v = volBuf->values()[ f ] * 0.01f; + m_portBuffer[f][0] *= v * l; + m_portBuffer[f][1] *= v * r; + } + } + + // only pan has s.ex.data: + else if( panBuf ) + { + float v = m_volumeModel->value() * 0.01f; + for( f_cnt_t f = 0; f < fpp; ++f ) + { + float p = panBuf->values()[ f ] * 0.01f; + m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v; + m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v; + } + } + + // neither has s.ex.data: + else + { + float p = m_panningModel->value() * 0.01f; + float v = m_volumeModel->value() * 0.01f; + for( f_cnt_t f = 0; f < fpp; ++f ) + { + m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v; + m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v; + } + } + } + + // has vol model only + else if( m_volumeModel ) + { + ValueBuffer * volBuf = m_volumeModel->valueBuffer(); + + if( volBuf ) + { + for( f_cnt_t f = 0; f < fpp; ++f ) + { + float v = volBuf->values()[ f ] * 0.01f; + m_portBuffer[f][0] *= v; + m_portBuffer[f][1] *= v; + } + } + else + { + float v = m_volumeModel->value() * 0.01f; + for( f_cnt_t f = 0; f < fpp; ++f ) + { + m_portBuffer[f][0] *= v; + m_portBuffer[f][1] *= v; + } + } + } + } + // as of now there's no situation where we only have panning model but no volume model + // if we have neither, we don't have to do anything here - just pass the audio as is + + // handle effects const bool me = processEffects(); if( me || m_bufferUsage ) { - engine::fxMixer()->mixToChannel( m_portBuffer, m_nextFxChannel ); + engine::fxMixer()->mixToChannel( m_portBuffer, m_nextFxChannel ); // send output to fx mixer + // TODO: improve the flow here - convert to pull model m_bufferUsage = false; } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index bbbe8ba1bb4..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" ) ), @@ -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() ); } 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" ) ); }