Merge pull request #911 from diizy/master-nph
Revision of handling of frameoffset for NPH, SPH
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#define AUTOMATABLE_MODEL_H
|
||||
|
||||
#include <math.h>
|
||||
#include <QtCore/QMutex>
|
||||
|
||||
#include "JournallingObject.h"
|
||||
#include "Model.h"
|
||||
@@ -103,9 +104,6 @@ public:
|
||||
{
|
||||
return isAutomated() || m_controllerConnection != NULL;
|
||||
}
|
||||
|
||||
bool hasSampleExactData() const;
|
||||
|
||||
|
||||
ControllerConnection* controllerConnection() const
|
||||
{
|
||||
@@ -142,10 +140,8 @@ public:
|
||||
|
||||
float controllerValue( int frameOffset ) const;
|
||||
|
||||
// returns sample-exact data as a ValueBuffer
|
||||
// should only be called when sample-exact data exists
|
||||
// in other cases (eg. for automation), the receiving end should interpolate
|
||||
// the values themselves
|
||||
//! @brief Function that returns sample-exact data as a ValueBuffer
|
||||
//! @return pointer to model's valueBuffer when s.ex.data exists, NULL otherwise
|
||||
ValueBuffer * valueBuffer();
|
||||
|
||||
template<class T>
|
||||
@@ -264,6 +260,16 @@ public:
|
||||
{
|
||||
m_hasStrictStepSize = b;
|
||||
}
|
||||
|
||||
static void incrementPeriodCounter()
|
||||
{
|
||||
++s_periodCounter;
|
||||
}
|
||||
|
||||
static void resetPeriodCounter()
|
||||
{
|
||||
s_periodCounter = 0;
|
||||
}
|
||||
|
||||
public slots:
|
||||
virtual void reset();
|
||||
@@ -332,6 +338,13 @@ private:
|
||||
static float s_copiedValue;
|
||||
|
||||
ValueBuffer m_valueBuffer;
|
||||
long m_lastUpdatedPeriod;
|
||||
static long s_periodCounter;
|
||||
|
||||
bool m_hasSampleExactData;
|
||||
|
||||
// prevent several threads from attempting to write the same vb at the same time
|
||||
QMutex m_valueBufferMutex;
|
||||
|
||||
signals:
|
||||
void initValueChanged( float val );
|
||||
|
||||
@@ -312,7 +312,6 @@ public:
|
||||
// audio-buffer-mgm
|
||||
void bufferToPort( const sampleFrame * _buf,
|
||||
const fpp_t _frames,
|
||||
const f_cnt_t _offset,
|
||||
stereoVolumeVector _volume_vector,
|
||||
AudioPort * _port );
|
||||
|
||||
|
||||
@@ -74,6 +74,16 @@ public:
|
||||
{
|
||||
return m_midiChannel;
|
||||
}
|
||||
|
||||
/*! convenience function that returns offset for the first period and zero otherwise,
|
||||
used by instruments to handle the offset: instruments have to check this property and
|
||||
add the correct number of empty frames in the beginning of the period */
|
||||
f_cnt_t noteOffset() const
|
||||
{
|
||||
return m_totalFramesPlayed == 0
|
||||
? offset()
|
||||
: 0;
|
||||
}
|
||||
|
||||
const float& frequency() const
|
||||
{
|
||||
@@ -94,7 +104,7 @@ public:
|
||||
/*! Returns whether playback of note is finished and thus handle can be deleted */
|
||||
virtual bool isFinished() const
|
||||
{
|
||||
return m_released && framesLeft() <= 0 && m_scheduledNoteOff < 0;
|
||||
return m_released && framesLeft() <= 0;
|
||||
}
|
||||
|
||||
/*! Returns number of frames left for playback */
|
||||
@@ -264,7 +274,6 @@ private:
|
||||
// played after release
|
||||
f_cnt_t m_releaseFramesDone; // number of frames done after
|
||||
// release of note
|
||||
f_cnt_t m_scheduledNoteOff; // variable for scheduling noteoff at next period
|
||||
NotePlayHandleList m_subNotes; // used for chords and arpeggios
|
||||
volatile bool m_released; // indicates whether note is released
|
||||
bool m_hasParent;
|
||||
|
||||
@@ -88,8 +88,8 @@ public:
|
||||
virtual void play( sampleFrame* buffer ) = 0;
|
||||
virtual bool isFinished( void ) const = 0;
|
||||
|
||||
// returns how many frames this play-handle is aligned ahead, i.e.
|
||||
// at which position it is inserted in the according buffer
|
||||
// returns the frameoffset at the start of the playhandle,
|
||||
// ie. how many empty frames should be inserted at the start of the first period
|
||||
f_cnt_t offset() const
|
||||
{
|
||||
return m_offset;
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SAMPLE_PLAY_HANDLE_H
|
||||
#define _SAMPLE_PLAY_HANDLE_H
|
||||
#ifndef SAMPLE_PLAY_HANDLE_H
|
||||
#define SAMPLE_PLAY_HANDLE_H
|
||||
|
||||
#include "Mixer.h"
|
||||
#include "SampleBuffer.h"
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void play( sampleFrame * _working_buffer );
|
||||
virtual void play( sampleFrame * buffer );
|
||||
virtual bool isFinished() const;
|
||||
|
||||
virtual bool isFromTrack( const track * _track ) const;
|
||||
|
||||
@@ -75,21 +75,10 @@ bool AmplifierEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames )
|
||||
const float d = dryLevel();
|
||||
const float w = wetLevel();
|
||||
|
||||
ValueBuffer * volBuf = m_ampControls.m_volumeModel.hasSampleExactData()
|
||||
? m_ampControls.m_volumeModel.valueBuffer()
|
||||
: NULL;
|
||||
|
||||
ValueBuffer * panBuf = m_ampControls.m_panModel.hasSampleExactData()
|
||||
? m_ampControls.m_panModel.valueBuffer()
|
||||
: NULL;
|
||||
|
||||
ValueBuffer * leftBuf = m_ampControls.m_leftModel.hasSampleExactData()
|
||||
? m_ampControls.m_leftModel.valueBuffer()
|
||||
: NULL;
|
||||
|
||||
ValueBuffer * rightBuf = m_ampControls.m_rightModel.hasSampleExactData()
|
||||
? m_ampControls.m_rightModel.valueBuffer()
|
||||
: NULL;
|
||||
ValueBuffer * volBuf = m_ampControls.m_volumeModel.valueBuffer();
|
||||
ValueBuffer * panBuf = m_ampControls.m_panModel.valueBuffer();
|
||||
ValueBuffer * leftBuf = m_ampControls.m_leftModel.valueBuffer();
|
||||
ValueBuffer * rightBuf = m_ampControls.m_rightModel.valueBuffer();
|
||||
|
||||
for( fpp_t f = 0; f < frames; ++f )
|
||||
{
|
||||
|
||||
@@ -119,6 +119,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
// Magic key - a frequency < 20 (say, the bottom piano note if using
|
||||
// a A4 base tuning) restarts the start point. The note is not actually
|
||||
@@ -165,14 +166,14 @@ void audioFileProcessor::playNote( NotePlayHandle * _n,
|
||||
|
||||
if( ! _n->isFinished() )
|
||||
{
|
||||
if( m_sampleBuffer.play( _working_buffer,
|
||||
if( m_sampleBuffer.play( _working_buffer + offset,
|
||||
(handleState *)_n->m_pluginData,
|
||||
frames, _n->frequency(),
|
||||
static_cast<SampleBuffer::LoopMode>( m_loopModel.value() ) ) )
|
||||
{
|
||||
applyRelease( _working_buffer, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer,
|
||||
frames,_n );
|
||||
frames + offset, _n );
|
||||
|
||||
emit isPlaying( ((handleState *)_n->m_pluginData)->frameIndex() );
|
||||
}
|
||||
|
||||
@@ -282,9 +282,10 @@ void bitInvader::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
bSynth * ps = static_cast<bSynth *>( _n->m_pluginData );
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
for( fpp_t frame = offset; frame < frames + offset; ++frame )
|
||||
{
|
||||
const sample_t cur = ps->nextStringSample();
|
||||
for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl )
|
||||
@@ -295,7 +296,7 @@ void bitInvader::playNote( NotePlayHandle * _n,
|
||||
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -163,6 +163,8 @@ typedef KickerOsc<DspEffectLibrary::MonoToStereoAdaptor<DistFX> > SweepOsc;
|
||||
void kickerInstrument::playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
const float decfr = m_decayModel.value() *
|
||||
engine::mixer()->processingSampleRate() / 1000.0f;
|
||||
const f_cnt_t tfp = _n->totalFramesPlayed();
|
||||
@@ -187,10 +189,8 @@ void kickerInstrument::playNote( NotePlayHandle * _n,
|
||||
_n->noteOff();
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
|
||||
SweepOsc * so = static_cast<SweepOsc *>( _n->m_pluginData );
|
||||
so->update( _working_buffer, frames, engine::mixer()->processingSampleRate() );
|
||||
so->update( _working_buffer + offset, frames, engine::mixer()->processingSampleRate() );
|
||||
|
||||
if( _n->isReleased() )
|
||||
{
|
||||
@@ -199,12 +199,12 @@ void kickerInstrument::playNote( NotePlayHandle * _n,
|
||||
for( fpp_t f = 0; f < frames; ++f )
|
||||
{
|
||||
const float fac = ( done+f < desired ) ? ( 1.0f - ( ( done+f ) / desired ) ) : 0;
|
||||
_working_buffer[f][0] *= fac;
|
||||
_working_buffer[f][1] *= fac;
|
||||
_working_buffer[f+offset][0] *= fac;
|
||||
_working_buffer[f+offset][1] *= fac;
|
||||
}
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1245,21 +1245,22 @@ MonstroInstrument::~MonstroInstrument()
|
||||
void MonstroInstrument::playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->offset();
|
||||
|
||||
if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
|
||||
{
|
||||
const sample_rate_t samplerate = m_samplerate;
|
||||
_n->m_pluginData = new MonstroSynth( this, _n, samplerate, m_fpp );
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
|
||||
MonstroSynth * ms = static_cast<MonstroSynth *>( _n->m_pluginData );
|
||||
|
||||
ms->renderOutput( frames, _working_buffer );
|
||||
ms->renderOutput( frames, _working_buffer + offset );
|
||||
|
||||
//applyRelease( _working_buffer, _n ); // we have our own release
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
void MonstroInstrument::deleteNotePluginData( NotePlayHandle * _n )
|
||||
|
||||
@@ -556,21 +556,22 @@ NesInstrument::~NesInstrument()
|
||||
|
||||
void NesInstrument::playNote( NotePlayHandle * n, sampleFrame * workingBuffer )
|
||||
{
|
||||
const fpp_t frames = n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = n->noteOffset();
|
||||
|
||||
if ( n->totalFramesPlayed() == 0 || n->m_pluginData == NULL )
|
||||
{
|
||||
NesObject * nes = new NesObject( this, engine::mixer()->processingSampleRate(), n, engine::mixer()->framesPerPeriod() );
|
||||
n->m_pluginData = nes;
|
||||
}
|
||||
|
||||
const fpp_t frames = n->framesLeftForCurrentPeriod();
|
||||
|
||||
NesObject * nes = static_cast<NesObject *>( n->m_pluginData );
|
||||
|
||||
nes->renderOutput( workingBuffer, frames );
|
||||
nes->renderOutput( workingBuffer + offset, frames );
|
||||
|
||||
applyRelease( workingBuffer, n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( workingBuffer, frames, n );
|
||||
instrumentTrack()->processAudioBuffer( workingBuffer, frames + offset, n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -227,6 +227,9 @@ QString organicInstrument::nodeName() const
|
||||
void organicInstrument::playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
if( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
|
||||
{
|
||||
Oscillator * oscs_l[m_numOscillators];
|
||||
@@ -296,10 +299,8 @@ void organicInstrument::playNote( NotePlayHandle * _n,
|
||||
Oscillator * osc_l = static_cast<oscPtr *>( _n->m_pluginData )->oscLeft;
|
||||
Oscillator * osc_r = static_cast<oscPtr *>( _n->m_pluginData)->oscRight;
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
|
||||
osc_l->update( _working_buffer, frames, 0 );
|
||||
osc_r->update( _working_buffer, frames, 1 );
|
||||
osc_l->update( _working_buffer + offset, frames, 0 );
|
||||
osc_r->update( _working_buffer + offset, frames, 1 );
|
||||
|
||||
|
||||
// -- fx section --
|
||||
@@ -317,7 +318,7 @@ void organicInstrument::playNote( NotePlayHandle * _n,
|
||||
|
||||
// -- --
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -238,6 +238,7 @@ void papuInstrument::playNote( NotePlayHandle * _n,
|
||||
const f_cnt_t tfp = _n->totalFramesPlayed();
|
||||
const int samplerate = engine::mixer()->processingSampleRate();
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
int data = 0;
|
||||
int freq = _n->frequency();
|
||||
@@ -400,12 +401,12 @@ void papuInstrument::playNote( NotePlayHandle * _n,
|
||||
for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
|
||||
{
|
||||
sample_t s = float(buf[frame*2+ch])/32768.0;
|
||||
_working_buffer[frames-framesleft+frame][ch] = s;
|
||||
_working_buffer[frames-framesleft+frame+offset][ch] = s;
|
||||
}
|
||||
}
|
||||
framesleft -= count;
|
||||
}
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -138,6 +138,7 @@ void patmanInstrument::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
if( !_n->m_pluginData )
|
||||
{
|
||||
@@ -148,12 +149,12 @@ void patmanInstrument::playNote( NotePlayHandle * _n,
|
||||
float play_freq = hdata->tuned ? _n->frequency() :
|
||||
hdata->sample->frequency();
|
||||
|
||||
if( hdata->sample->play( _working_buffer, hdata->state, frames,
|
||||
if( hdata->sample->play( _working_buffer + offset, hdata->state, frames,
|
||||
play_freq, m_loopedModel.value() ? SampleBuffer::LoopOn : SampleBuffer::LoopOff ) )
|
||||
{
|
||||
applyRelease( _working_buffer, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer,
|
||||
frames, _n );
|
||||
frames + offset, _n );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -454,6 +454,7 @@ void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe
|
||||
float currentSampleRate = engine::mixer()->processingSampleRate();
|
||||
|
||||
fpp_t frameNum = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
|
||||
{
|
||||
_n->m_pluginData = new SfxrSynth( this );
|
||||
@@ -477,7 +478,7 @@ void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe
|
||||
{
|
||||
for( ch_cnt_t j=0; j<DEFAULT_CHANNELS; j++ )
|
||||
{
|
||||
_working_buffer[i][j] = pitchedBuffer[i*pitchedFrameNum/frameNum][j];
|
||||
_working_buffer[i+offset][j] = pitchedBuffer[i*pitchedFrameNum/frameNum][j];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +486,7 @@ void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe
|
||||
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frameNum, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frameNum + offset, _n );
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -317,6 +317,7 @@ void sidInstrument::playNote( NotePlayHandle * _n,
|
||||
_n->m_pluginData = sid;
|
||||
}
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
cSID *sid = static_cast<cSID *>( _n->m_pluginData );
|
||||
int delta_t = clockrate * frames / samplerate + 4;
|
||||
@@ -430,11 +431,11 @@ void sidInstrument::playNote( NotePlayHandle * _n,
|
||||
sample_t s = float(buf[frame])/32768.0;
|
||||
for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
|
||||
{
|
||||
_working_buffer[frame][ch] = s;
|
||||
_working_buffer[frame+offset][ch] = s;
|
||||
}
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -265,6 +265,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
malletsSynth * ps = static_cast<malletsSynth *>( _n->m_pluginData );
|
||||
ps->setFrequency( freq );
|
||||
@@ -274,7 +275,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n,
|
||||
{
|
||||
add_scale = static_cast<sample_t>( m_strikeModel.value() ) * freq * 2.5f;
|
||||
}
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
for( fpp_t frame = offset; frame < frames + offset; ++frame )
|
||||
{
|
||||
_working_buffer[frame][0] = ps->nextSampleLeft() *
|
||||
( m_scalers[m_presetsModel.value()] + add_scale );
|
||||
@@ -282,7 +283,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n,
|
||||
( m_scalers[m_presetsModel.value()] + add_scale );
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -358,13 +358,14 @@ void TripleOscillator::playNote( NotePlayHandle * _n,
|
||||
Oscillator * osc_r = static_cast<oscPtr *>( _n->m_pluginData )->oscRight;
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
|
||||
osc_l->update( _working_buffer, frames, 0 );
|
||||
osc_r->update( _working_buffer, frames, 1 );
|
||||
osc_l->update( _working_buffer + offset, frames, 0 );
|
||||
osc_r->update( _working_buffer + offset, frames, 1 );
|
||||
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -302,10 +302,11 @@ void vibed::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer )
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
stringContainer * ps = static_cast<stringContainer *>(
|
||||
_n->m_pluginData );
|
||||
|
||||
for( fpp_t i = 0; i < frames; ++i )
|
||||
for( fpp_t i = offset; i < frames + offset; ++i )
|
||||
{
|
||||
_working_buffer[i][0] = 0.0f;
|
||||
_working_buffer[i][1] = 0.0f;
|
||||
@@ -324,7 +325,7 @@ void vibed::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer )
|
||||
}
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ void WatsynInstrument::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
const fpp_t frames = _n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = _n->noteOffset();
|
||||
sampleFrame * buffer = _working_buffer + offset;
|
||||
|
||||
WatsynObject * w = static_cast<WatsynObject *>( _n->m_pluginData );
|
||||
|
||||
@@ -424,9 +426,9 @@ void WatsynInstrument::playNote( NotePlayHandle * _n,
|
||||
const float amix = 1.0 - bmix;
|
||||
|
||||
// mix a/b streams according to mixing knob
|
||||
_working_buffer[f][0] = ( abuf[f][0] * amix ) +
|
||||
buffer[f][0] = ( abuf[f][0] * amix ) +
|
||||
( bbuf[f][0] * bmix );
|
||||
_working_buffer[f][1] = ( abuf[f][1] * amix ) +
|
||||
buffer[f][1] = ( abuf[f][1] * amix ) +
|
||||
( bbuf[f][1] * bmix );
|
||||
}
|
||||
}
|
||||
@@ -440,16 +442,16 @@ void WatsynInstrument::playNote( NotePlayHandle * _n,
|
||||
for( fpp_t f=0; f < frames; f++ )
|
||||
{
|
||||
// mix a/b streams according to mixing knob
|
||||
_working_buffer[f][0] = ( abuf[f][0] * amix ) +
|
||||
buffer[f][0] = ( abuf[f][0] * amix ) +
|
||||
( bbuf[f][0] * bmix );
|
||||
_working_buffer[f][1] = ( abuf[f][1] * amix ) +
|
||||
buffer[f][1] = ( abuf[f][1] * amix ) +
|
||||
( bbuf[f][1] * bmix );
|
||||
}
|
||||
}
|
||||
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "lmms_math.h"
|
||||
|
||||
float AutomatableModel::s_copiedValue = 0;
|
||||
|
||||
long AutomatableModel::s_periodCounter = 0;
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,9 @@ AutomatableModel::AutomatableModel( DataType type,
|
||||
m_hasStrictStepSize( false ),
|
||||
m_hasLinkedModels( false ),
|
||||
m_controllerConnection( NULL ),
|
||||
m_valueBuffer( static_cast<int>( engine::mixer()->framesPerPeriod() ) )
|
||||
m_valueBuffer( static_cast<int>( engine::mixer()->framesPerPeriod() ) ),
|
||||
m_lastUpdatedPeriod( -1 ),
|
||||
m_hasSampleExactData( false )
|
||||
|
||||
{
|
||||
setInitValue( val );
|
||||
@@ -86,39 +88,6 @@ bool AutomatableModel::isAutomated() const
|
||||
return AutomationPattern::isAutomated( this );
|
||||
}
|
||||
|
||||
bool AutomatableModel::hasSampleExactData() const
|
||||
{
|
||||
// if a controller is connected...
|
||||
if( m_controllerConnection != NULL )
|
||||
{
|
||||
// ...and is sample-exact, then return true
|
||||
if( m_controllerConnection->getController()->isSampleExact() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// check also the same for the first linked model
|
||||
if( hasLinkedModels() )
|
||||
{
|
||||
AutomatableModel* lm = m_linkedModels.first();
|
||||
if( lm->m_controllerConnection != NULL )
|
||||
{
|
||||
if( lm->m_controllerConnection->getController()->isSampleExact() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we have values we can interpolate return true
|
||||
if( m_oldValue != m_value )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise, return false
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, const QString& name )
|
||||
{
|
||||
@@ -554,6 +523,23 @@ float AutomatableModel::controllerValue( int frameOffset ) const
|
||||
|
||||
ValueBuffer * AutomatableModel::valueBuffer()
|
||||
{
|
||||
// if we've already calculated the valuebuffer this period, return the cached buffer
|
||||
if( m_lastUpdatedPeriod == s_periodCounter )
|
||||
{
|
||||
return m_hasSampleExactData
|
||||
? &m_valueBuffer
|
||||
: NULL;
|
||||
}
|
||||
QMutexLocker m( &m_valueBufferMutex );
|
||||
if( m_lastUpdatedPeriod == s_periodCounter )
|
||||
{
|
||||
return m_hasSampleExactData
|
||||
? &m_valueBuffer
|
||||
: NULL;
|
||||
}
|
||||
|
||||
float val = m_value; // make sure our m_value doesn't change midway
|
||||
|
||||
ValueBuffer * vb;
|
||||
if( m_controllerConnection && m_controllerConnection->getController()->isSampleExact() )
|
||||
{
|
||||
@@ -581,6 +567,8 @@ ValueBuffer * AutomatableModel::valueBuffer()
|
||||
"lacks implementation for a scale type");
|
||||
break;
|
||||
}
|
||||
m_lastUpdatedPeriod = s_periodCounter;
|
||||
m_hasSampleExactData = true;
|
||||
return &m_valueBuffer;
|
||||
}
|
||||
}
|
||||
@@ -598,20 +586,25 @@ ValueBuffer * AutomatableModel::valueBuffer()
|
||||
{
|
||||
nvalues[i] = fittedValue( values[i], false );
|
||||
}
|
||||
m_lastUpdatedPeriod = s_periodCounter;
|
||||
m_hasSampleExactData = true;
|
||||
return &m_valueBuffer;
|
||||
}
|
||||
|
||||
if( m_oldValue != m_value )
|
||||
if( m_oldValue != val )
|
||||
{
|
||||
m_valueBuffer.interpolate( m_oldValue, m_value );
|
||||
m_oldValue = m_value;
|
||||
m_valueBuffer.interpolate( m_oldValue, val );
|
||||
m_oldValue = val;
|
||||
m_lastUpdatedPeriod = s_periodCounter;
|
||||
m_hasSampleExactData = true;
|
||||
return &m_valueBuffer;
|
||||
}
|
||||
|
||||
// if we have no sample-exact source for a ValueBuffer, create one and fill it with current value
|
||||
// ideally, recipients should check first if we hasSampleExactData before fetching ValueBuffers
|
||||
m_valueBuffer.fill( m_value );
|
||||
return &m_valueBuffer;
|
||||
// if we have no sample-exact source for a ValueBuffer, return NULL to signify that no data is available at the moment
|
||||
// in which case the recipient knows to use the static value() instead
|
||||
m_lastUpdatedPeriod = s_periodCounter;
|
||||
m_hasSampleExactData = false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -126,12 +126,8 @@ void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
if( sender->m_hasInput || sender->m_stillRunning )
|
||||
{
|
||||
// figure out if we're getting sample-exact input
|
||||
ValueBuffer * sendBuf = sendModel->hasSampleExactData()
|
||||
? sendModel->valueBuffer()
|
||||
: NULL;
|
||||
ValueBuffer * volBuf = sender->m_volumeModel.hasSampleExactData()
|
||||
? sender->m_volumeModel.valueBuffer()
|
||||
: NULL;
|
||||
ValueBuffer * sendBuf = sendModel->valueBuffer();
|
||||
ValueBuffer * volBuf = sender->m_volumeModel.valueBuffer();
|
||||
|
||||
// mix it's output with this one's output
|
||||
sampleFrame * ch_buf = sender->m_buffer;
|
||||
@@ -526,9 +522,7 @@ void FxMixer::masterMix( sampleFrame * _buf )
|
||||
//m_sendsMutex.unlock();
|
||||
|
||||
// handle sample-exact data in master volume fader
|
||||
ValueBuffer * volBuf = m_fxChannels[0]->m_volumeModel.hasSampleExactData()
|
||||
? m_fxChannels[0]->m_volumeModel.valueBuffer()
|
||||
: NULL;
|
||||
ValueBuffer * volBuf = m_fxChannels[0]->m_volumeModel.valueBuffer();
|
||||
|
||||
if( volBuf )
|
||||
{
|
||||
|
||||
@@ -133,7 +133,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
|
||||
|
||||
if( n->isReleased() == false )
|
||||
{
|
||||
envReleaseBegin += engine::mixer()->framesPerPeriod();
|
||||
envReleaseBegin += frames;
|
||||
}
|
||||
|
||||
// because of optimizations, there's special code for several cases:
|
||||
|
||||
@@ -109,7 +109,7 @@ void LfoController::updateValueBuffer()
|
||||
? m_sampleFunction( phase )
|
||||
: m_userDefSampleBuffer->userWaveSample( phase );
|
||||
|
||||
values[i] = m_baseModel.value() + ( m_amountModel.value() * currentSample / 2.0f );
|
||||
values[i] = qBound( 0.0f, m_baseModel.value() + ( m_amountModel.value() * currentSample / 2.0f ), 1.0f );
|
||||
|
||||
phase += 1.0 / m_duration;
|
||||
}
|
||||
|
||||
@@ -418,6 +418,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
// and trigger LFOs
|
||||
EnvelopeAndLfoParameters::instances()->trigger();
|
||||
Controller::triggerFrameCounter();
|
||||
AutomatableModel::incrementPeriodCounter();
|
||||
|
||||
const float new_cpu_load = timer.elapsed() / 10000.0f *
|
||||
processingSampleRate() / m_framesPerPeriod;
|
||||
@@ -451,44 +452,40 @@ void Mixer::clear()
|
||||
|
||||
|
||||
|
||||
void Mixer::bufferToPort( const sampleFrame * _buf,
|
||||
const fpp_t _frames,
|
||||
const f_cnt_t _offset,
|
||||
stereoVolumeVector _vv,
|
||||
AudioPort * _port )
|
||||
void Mixer::bufferToPort( const sampleFrame * buf,
|
||||
const fpp_t frames,
|
||||
stereoVolumeVector vv,
|
||||
AudioPort * port )
|
||||
{
|
||||
const int start_frame = _offset % m_framesPerPeriod;
|
||||
int end_frame = start_frame + _frames;
|
||||
const int loop1_frame = qMin<int>( end_frame, m_framesPerPeriod );
|
||||
const int loop1_frame = qMin<int>( frames, m_framesPerPeriod );
|
||||
|
||||
_port->lockFirstBuffer();
|
||||
MixHelpers::addMultipliedStereo( _port->firstBuffer()+start_frame, // dst
|
||||
_buf, // src
|
||||
_vv.vol[0], _vv.vol[1], // coeff left/right
|
||||
loop1_frame - start_frame ); // frame count
|
||||
_port->unlockFirstBuffer();
|
||||
port->lockFirstBuffer();
|
||||
MixHelpers::addMultipliedStereo( port->firstBuffer(), // dst
|
||||
buf, // src
|
||||
vv.vol[0], vv.vol[1], // coeff left/right
|
||||
loop1_frame ); // frame count
|
||||
port->unlockFirstBuffer();
|
||||
|
||||
_port->lockSecondBuffer();
|
||||
if( end_frame > m_framesPerPeriod )
|
||||
if( frames > m_framesPerPeriod )
|
||||
{
|
||||
const int frames_done = m_framesPerPeriod - start_frame;
|
||||
end_frame -= m_framesPerPeriod;
|
||||
end_frame = qMin<int>( end_frame, m_framesPerPeriod );
|
||||
port->lockSecondBuffer();
|
||||
|
||||
const fpp_t framesLeft = qMin<int>( frames - m_framesPerPeriod, m_framesPerPeriod );
|
||||
|
||||
MixHelpers::addMultipliedStereo( _port->secondBuffer(), // dst
|
||||
_buf+frames_done, // src
|
||||
_vv.vol[0], _vv.vol[1], // coeff left/right
|
||||
end_frame ); // frame count
|
||||
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->m_bufferUsage = AudioPort::BothBuffers;
|
||||
port->unlockSecondBuffer();
|
||||
}
|
||||
else if( _port->m_bufferUsage == AudioPort::NoUsage )
|
||||
else if( port->m_bufferUsage == AudioPort::NoUsage )
|
||||
{
|
||||
// only first buffer touched
|
||||
_port->m_bufferUsage = AudioPort::FirstBuffer;
|
||||
port->m_bufferUsage = AudioPort::FirstBuffer;
|
||||
}
|
||||
_port->unlockSecondBuffer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
m_framesBeforeRelease( 0 ),
|
||||
m_releaseFramesToDo( 0 ),
|
||||
m_releaseFramesDone( 0 ),
|
||||
m_scheduledNoteOff( -1 ),
|
||||
m_released( false ),
|
||||
m_hasParent( parent != NULL ),
|
||||
m_hadChildren( false ),
|
||||
@@ -119,13 +118,6 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
NotePlayHandle::~NotePlayHandle()
|
||||
{
|
||||
noteOff( 0 );
|
||||
if( m_scheduledNoteOff >= 0 ) // ensure that scheduled noteoffs get triggered if somehow the nph got destructed prematurely
|
||||
{
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
|
||||
MidiTime::fromFrames( m_scheduledNoteOff, engine::framesPerTick() ),
|
||||
m_scheduledNoteOff );
|
||||
}
|
||||
|
||||
if( hasParent() == false )
|
||||
{
|
||||
@@ -190,23 +182,20 @@ int NotePlayHandle::midiKey() const
|
||||
|
||||
void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
{
|
||||
if( m_scheduledNoteOff >= 0 ) // always trigger scheduled noteoffs, because they're only scheduled if the note is released
|
||||
{
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
|
||||
MidiTime::fromFrames( m_scheduledNoteOff, engine::framesPerTick() ),
|
||||
m_scheduledNoteOff );
|
||||
m_scheduledNoteOff = -1;
|
||||
}
|
||||
|
||||
if( m_muted )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// number of frames that can be played this period
|
||||
f_cnt_t framesThisPeriod = m_totalFramesPlayed == 0
|
||||
? engine::mixer()->framesPerPeriod() - offset()
|
||||
: engine::mixer()->framesPerPeriod();
|
||||
|
||||
// check if we start release during this period
|
||||
if( m_released == false &&
|
||||
instrumentTrack()->isSustainPedalPressed() == false &&
|
||||
m_totalFramesPlayed + engine::mixer()->framesPerPeriod() > m_frames )
|
||||
m_totalFramesPlayed + framesThisPeriod > m_frames )
|
||||
{
|
||||
noteOff( m_frames - m_totalFramesPlayed );
|
||||
}
|
||||
@@ -216,13 +205,18 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
// decreasing release of an instrument-track while the note is active
|
||||
if( framesLeft() > 0 )
|
||||
{
|
||||
// clear offset frames if we're at the first period
|
||||
if( m_totalFramesPlayed == 0 )
|
||||
{
|
||||
memset( _working_buffer, 0, sizeof( sampleFrame ) * offset() );
|
||||
}
|
||||
// play note!
|
||||
m_instrumentTrack->playNote( this, _working_buffer );
|
||||
}
|
||||
|
||||
if( m_released )
|
||||
{
|
||||
f_cnt_t todo = engine::mixer()->framesPerPeriod();
|
||||
f_cnt_t todo = framesThisPeriod;
|
||||
|
||||
// if this note is base-note for arpeggio, always set
|
||||
// m_releaseFramesToDo to bigger value than m_releaseFramesDone
|
||||
@@ -238,8 +232,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
{
|
||||
// yes, then look whether these samples can be played
|
||||
// within one audio-buffer
|
||||
if( m_framesBeforeRelease <=
|
||||
engine::mixer()->framesPerPeriod() )
|
||||
if( m_framesBeforeRelease <= framesThisPeriod )
|
||||
{
|
||||
// yes, then we did less releaseFramesDone
|
||||
todo -= m_framesBeforeRelease;
|
||||
@@ -251,8 +244,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
// and wait for next loop... (we're not in
|
||||
// release-phase yet)
|
||||
todo = 0;
|
||||
m_framesBeforeRelease -=
|
||||
engine::mixer()->framesPerPeriod();
|
||||
m_framesBeforeRelease -= framesThisPeriod;
|
||||
}
|
||||
}
|
||||
// look whether we're in release-phase
|
||||
@@ -290,7 +282,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
}
|
||||
|
||||
// update internal data
|
||||
m_totalFramesPlayed += engine::mixer()->framesPerPeriod();
|
||||
m_totalFramesPlayed += framesThisPeriod;
|
||||
}
|
||||
|
||||
|
||||
@@ -318,6 +310,10 @@ f_cnt_t NotePlayHandle::framesLeft() const
|
||||
|
||||
fpp_t NotePlayHandle::framesLeftForCurrentPeriod() const
|
||||
{
|
||||
if( m_totalFramesPlayed == 0 )
|
||||
{
|
||||
return (fpp_t) qMin<f_cnt_t>( framesLeft(), engine::mixer()->framesPerPeriod() - offset() );
|
||||
}
|
||||
return (fpp_t) qMin<f_cnt_t>( framesLeft(), engine::mixer()->framesPerPeriod() );
|
||||
}
|
||||
|
||||
@@ -352,18 +348,10 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
|
||||
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
|
||||
{
|
||||
// send MidiNoteOff event
|
||||
f_cnt_t realOffset = offset() + _s; // get actual frameoffset of release, in global time
|
||||
if( realOffset < engine::mixer()->framesPerPeriod() ) // if release happens during this period, trigger midievent
|
||||
{
|
||||
m_instrumentTrack->processOutEvent(
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
|
||||
MidiTime::fromFrames( realOffset, engine::framesPerTick() ),
|
||||
realOffset );
|
||||
}
|
||||
else // if release flows over to next period, use m_scheduledNoteOff to trigger it later
|
||||
{
|
||||
m_scheduledNoteOff = realOffset - engine::mixer()->framesPerPeriod();
|
||||
}
|
||||
MidiTime::fromFrames( _s, engine::framesPerTick() ),
|
||||
_s );
|
||||
}
|
||||
|
||||
// inform attached components about MIDI finished (used for recording in Piano Roll)
|
||||
|
||||
@@ -96,7 +96,7 @@ SamplePlayHandle::~SamplePlayHandle()
|
||||
|
||||
|
||||
|
||||
void SamplePlayHandle::play( sampleFrame * _working_buffer )
|
||||
void SamplePlayHandle::play( sampleFrame * buffer )
|
||||
{
|
||||
//play( 0, _try_parallelizing );
|
||||
if( framesDone() >= totalFrames() )
|
||||
@@ -104,17 +104,27 @@ void SamplePlayHandle::play( sampleFrame * _working_buffer )
|
||||
return;
|
||||
}
|
||||
|
||||
const fpp_t frames = engine::mixer()->framesPerPeriod();
|
||||
sampleFrame * workingBuffer = buffer;
|
||||
const fpp_t fpp = engine::mixer()->framesPerPeriod();
|
||||
f_cnt_t frames = fpp;
|
||||
|
||||
// apply offset for the first period
|
||||
if( framesDone() == 0 )
|
||||
{
|
||||
buffer += offset();
|
||||
frames -= offset();
|
||||
}
|
||||
|
||||
if( !( m_track && m_track->isMuted() )
|
||||
&& !( m_bbTrack && m_bbTrack->isMuted() ) )
|
||||
{
|
||||
stereoVolumeVector v =
|
||||
{ { m_volumeModel->value() / DefaultVolume,
|
||||
m_volumeModel->value() / DefaultVolume } };
|
||||
m_sampleBuffer->play( _working_buffer, &m_state, frames,
|
||||
m_sampleBuffer->play( workingBuffer, &m_state, frames,
|
||||
BaseFreq );
|
||||
engine::mixer()->bufferToPort( _working_buffer, frames,
|
||||
offset(), v, m_audioPort );
|
||||
engine::mixer()->bufferToPort( buffer, fpp,
|
||||
v, m_audioPort );
|
||||
}
|
||||
|
||||
m_frame += frames;
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
#include "tab_widget.h"
|
||||
#include "tooltip.h"
|
||||
#include "track_label_button.h"
|
||||
|
||||
#include "ValueBuffer.h"
|
||||
|
||||
|
||||
const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack",
|
||||
@@ -186,33 +186,58 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames,
|
||||
// now
|
||||
m_audioPort.effects()->startRunning();
|
||||
|
||||
float v_scale = (float) getVolume() / DefaultVolume;
|
||||
// get volume knob data
|
||||
static const float DefaultVolumeRatio = 1.0f / DefaultVolume;
|
||||
ValueBuffer * volBuf = m_volumeModel.valueBuffer();
|
||||
float v_scale = volBuf
|
||||
? 1.0f
|
||||
: getVolume() * DefaultVolumeRatio;
|
||||
|
||||
// instruments using instrument-play-handles will call this method
|
||||
// without any knowledge about notes, so they pass NULL for n, which
|
||||
// is no problem for us since we just bypass the envelopes+LFOs
|
||||
if( m_instrument->flags().testFlag( Instrument::IsSingleStreamed ) == false && n != NULL )
|
||||
{
|
||||
m_soundShaping.processAudioBuffer( buf, frames, n );
|
||||
v_scale *= ( (float) n->getVolume() / DefaultVolume );
|
||||
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() );
|
||||
|
||||
int framesToMix = frames;
|
||||
int offset = 0;
|
||||
int panning = m_panningModel.value();
|
||||
// get panning knob data
|
||||
ValueBuffer * panBuf = m_panningModel.valueBuffer();
|
||||
int panning = panBuf
|
||||
? 0
|
||||
: m_panningModel.value();
|
||||
|
||||
if( n )
|
||||
{
|
||||
framesToMix = qMin<f_cnt_t>( n->framesLeftForCurrentPeriod(), framesToMix );
|
||||
offset = n->offset();
|
||||
|
||||
panning += n->getPanning();
|
||||
panning = tLimit<int>( panning, PanningLeft, PanningRight );
|
||||
}
|
||||
|
||||
engine::mixer()->bufferToPort( buf, framesToMix, offset, panningToVolumeVector( panning, v_scale ), &m_audioPort );
|
||||
// apply sample-exact volume/panning data
|
||||
if( volBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < frames; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
buf[f][0] *= v;
|
||||
buf[f][1] *= v;
|
||||
}
|
||||
}
|
||||
if( panBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < frames; ++f )
|
||||
{
|
||||
float p = panBuf->values()[ f ] * 0.01f;
|
||||
buf[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p );
|
||||
buf[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p );
|
||||
}
|
||||
}
|
||||
|
||||
engine::mixer()->bufferToPort( buf, frames, panningToVolumeVector( panning, v_scale ), &m_audioPort );
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user