Merge branch 'stable-1.1'
Conflicts: include/basic_filters.h src/core/FxMixer.cpp
|
Before Width: | Height: | Size: 1022 B After Width: | Height: | Size: 817 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
data/themes/default/receive_bg_arrow.png
Normal file
|
After Width: | Height: | Size: 289 B |
|
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 316 B |
@@ -61,10 +61,14 @@ public:
|
||||
static const int FxLineHeight;
|
||||
|
||||
private:
|
||||
static void drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name, bool isActive, bool sendToThis, bool receiveFromThis );
|
||||
|
||||
FxMixerView * m_mv;
|
||||
LcdWidget* m_lcd;
|
||||
int m_channelIndex;
|
||||
QBrush m_backgroundActive;
|
||||
static QPixmap * s_sendBgArrow;
|
||||
static QPixmap * s_receiveBgArrow;
|
||||
|
||||
private slots:
|
||||
void renameChannel();
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
#include "ThreadableJob.h"
|
||||
|
||||
|
||||
|
||||
class FxRoute;
|
||||
typedef QVector<FxRoute *> FxRouteVector;
|
||||
|
||||
class FxChannel : public ThreadableJob
|
||||
{
|
||||
@@ -58,11 +59,10 @@ class FxChannel : public ThreadableJob
|
||||
bool m_queued; // are we queued up for rendering yet?
|
||||
|
||||
// pointers to other channels that this one sends to
|
||||
QVector<fx_ch_t> m_sends;
|
||||
QVector<FloatModel *> m_sendAmount;
|
||||
FxRouteVector m_sends;
|
||||
|
||||
// pointers to other channels that send to this one
|
||||
QVector<fx_ch_t> m_receives;
|
||||
FxRouteVector m_receives;
|
||||
|
||||
virtual bool requiresProcessing() const { return true; }
|
||||
|
||||
@@ -71,6 +71,45 @@ class FxChannel : public ThreadableJob
|
||||
};
|
||||
|
||||
|
||||
class FxRoute : public QObject
|
||||
{
|
||||
public:
|
||||
FxRoute( FxChannel * from, FxChannel * to, float amount );
|
||||
virtual ~FxRoute();
|
||||
|
||||
fx_ch_t senderIndex() const
|
||||
{
|
||||
return m_from->m_channelIndex;
|
||||
}
|
||||
|
||||
fx_ch_t receiverIndex() const
|
||||
{
|
||||
return m_to->m_channelIndex;
|
||||
}
|
||||
|
||||
FloatModel * amount() const
|
||||
{
|
||||
return m_amount;
|
||||
}
|
||||
|
||||
FxChannel * sender() const
|
||||
{
|
||||
return m_from;
|
||||
}
|
||||
|
||||
FxChannel * receiver() const
|
||||
{
|
||||
return m_to;
|
||||
}
|
||||
|
||||
void updateName();
|
||||
|
||||
private:
|
||||
FxChannel * m_from;
|
||||
FxChannel * m_to;
|
||||
FloatModel * m_amount;
|
||||
};
|
||||
|
||||
|
||||
class EXPORT FxMixer : public JournallingObject, public Model
|
||||
{
|
||||
@@ -100,13 +139,16 @@ public:
|
||||
// it is safe to call even if the send already exists
|
||||
void createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel,
|
||||
float amount = 1.0f);
|
||||
void createRoute( FxChannel * from, FxChannel * to, float amount );
|
||||
|
||||
// delete the connection made by createChannelSend
|
||||
void deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel);
|
||||
void deleteChannelSend( FxRoute * route );
|
||||
|
||||
// determine if adding a send from sendFrom to
|
||||
// sendTo would result in an infinite mixer loop.
|
||||
bool isInfiniteLoop(fx_ch_t fromChannel, fx_ch_t toChannel);
|
||||
bool checkInfiniteLoop( FxChannel * from, FxChannel * to );
|
||||
|
||||
// return the FloatModel of fromChannel sending its output to the input of
|
||||
// toChannel. NULL if there is no send.
|
||||
@@ -129,11 +171,16 @@ public:
|
||||
// reset a channel's name, fx, sends, etc
|
||||
void clearChannel(fx_ch_t channelIndex);
|
||||
|
||||
// rename channels when moving etc. if they still have their original name
|
||||
void validateChannelName( int index, int oldIndex );
|
||||
|
||||
inline fx_ch_t numChannels() const
|
||||
{
|
||||
return m_fxChannels.size();
|
||||
}
|
||||
|
||||
FxRouteVector m_fxRoutes;
|
||||
|
||||
private:
|
||||
// the fx channels in the mixer. index 0 is always master.
|
||||
QVector<FxChannel *> m_fxChannels;
|
||||
@@ -142,7 +189,7 @@ private:
|
||||
void allocateChannelsTo(int num);
|
||||
QMutex m_sendsMutex;
|
||||
|
||||
void addChannelLeaf( int _ch, sampleFrame * _buf );
|
||||
void addChannelLeaf( FxChannel * ch, sampleFrame * buf );
|
||||
|
||||
friend class MixerWorkerThread;
|
||||
friend class FxMixerView;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* basic_filters.h - simple but powerful filter-class with most used filters
|
||||
*
|
||||
* original file by ???
|
||||
* original file by ???
|
||||
* modified and enhanced by Tobias Doerffel
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -26,8 +26,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _BASIC_FILTERS_H
|
||||
#define _BASIC_FILTERS_H
|
||||
#ifndef BASIC_FILTERS_H
|
||||
#define BASIC_FILTERS_H
|
||||
|
||||
#ifndef __USE_XOPEN
|
||||
#define __USE_XOPEN
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "Mixer.h"
|
||||
#include "templates.h"
|
||||
#include "lmms_constants.h"
|
||||
#include "interpolation.h"
|
||||
|
||||
//#include <iostream>
|
||||
//#include <cstdlib>
|
||||
@@ -127,16 +128,16 @@ public:
|
||||
// reset in/out history for simple filters
|
||||
m_ou1[_chnl] = m_ou2[_chnl] = m_in1[_chnl] =
|
||||
m_in2[_chnl] = 0.0f;
|
||||
|
||||
|
||||
// reset in/out history for moog-filter
|
||||
m_y1[_chnl] = m_y2[_chnl] = m_y3[_chnl] = m_y4[_chnl] =
|
||||
m_oldx[_chnl] = m_oldy1[_chnl] =
|
||||
m_oldy2[_chnl] = m_oldy3[_chnl] = 0.0f;
|
||||
|
||||
|
||||
// reset in/out history for RC-filters
|
||||
m_rclp0[_chnl] = m_rcbp0[_chnl] = m_rchp0[_chnl] = m_rclast0[_chnl] = 0.0f;
|
||||
m_rclp1[_chnl] = m_rcbp1[_chnl] = m_rchp1[_chnl] = m_rclast1[_chnl] = 0.0f;
|
||||
|
||||
|
||||
for(int i=0; i<6; i++)
|
||||
m_vfbp[i][_chnl] = m_vfhp[i][_chnl] = m_vflast[i][_chnl] = 0.0f;
|
||||
}
|
||||
@@ -186,130 +187,135 @@ public:
|
||||
// (C) 1998 ... 2009 S.Fendt. Released under the GPL v2.0 or any later version.
|
||||
|
||||
case Lowpass_RC12:
|
||||
case Bandpass_RC12:
|
||||
case Highpass_RC12:
|
||||
{
|
||||
sample_t lp, hp, bp;
|
||||
|
||||
sample_t in;
|
||||
|
||||
// 4-times oversampled... (even the moog-filter would benefit from this)
|
||||
sample_t lp, bp, hp, in;
|
||||
for( int n = 4; n != 0; --n )
|
||||
{
|
||||
in = _in0 + m_rcbp0[_chnl] * m_rcq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
lp = in * m_rcb + m_rclp0[_chnl] * m_rca;
|
||||
lp = (lp > +1.f) ? +1.f : lp;
|
||||
lp = (lp < -1.f) ? -1.f : lp;
|
||||
lp = qBound( -1.0f, lp, 1.0f );
|
||||
|
||||
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_rclast0[_chnl] = in;
|
||||
m_rclp0[_chnl] = lp;
|
||||
m_rchp0[_chnl] = hp;
|
||||
m_rcbp0[_chnl] = bp;
|
||||
}
|
||||
return lp;
|
||||
break;
|
||||
}
|
||||
case Highpass_RC12:
|
||||
case Bandpass_RC12:
|
||||
{
|
||||
sample_t hp, bp, in;
|
||||
for( int n = 4; n != 0; --n )
|
||||
{
|
||||
in = _in0 + m_rcbp0[_chnl] * m_rcq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
if( m_type == Lowpass_RC12 )
|
||||
out = lp;
|
||||
else if( m_type == Bandpass_RC12 )
|
||||
out = bp;
|
||||
else
|
||||
out = hp;
|
||||
|
||||
return( out );
|
||||
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_rclast0[_chnl] = in;
|
||||
m_rchp0[_chnl] = hp;
|
||||
m_rcbp0[_chnl] = bp;
|
||||
}
|
||||
return m_type == Highpass_RC12 ? hp : bp;
|
||||
break;
|
||||
}
|
||||
|
||||
case Lowpass_RC24:
|
||||
case Bandpass_RC24:
|
||||
case Highpass_RC24:
|
||||
{
|
||||
sample_t lp, hp, bp;
|
||||
|
||||
sample_t in;
|
||||
|
||||
sample_t lp, bp, hp, in;
|
||||
for( int n = 4; n != 0; --n )
|
||||
{
|
||||
// first stage is as for the 12dB case...
|
||||
in = _in0 + m_rcbp0[_chnl] * m_rcq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
lp = in * m_rcb + m_rclp0[_chnl] * m_rca;
|
||||
lp = (lp > +1.f) ? +1.f : lp;
|
||||
lp = (lp < -1.f) ? -1.f : lp;
|
||||
lp = qBound( -1.0f, lp, 1.0f );
|
||||
|
||||
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_rclast0[_chnl] = in;
|
||||
m_rclp0[_chnl] = lp;
|
||||
m_rcbp0[_chnl] = bp;
|
||||
m_rchp0[_chnl] = hp;
|
||||
|
||||
// second stage gets the output of the first stage as input...
|
||||
in = lp + m_rcbp1[_chnl] * m_rcq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
lp = in * m_rcb + m_rclp1[_chnl] * m_rca;
|
||||
lp = qBound( -1.0f, lp, 1.0f );
|
||||
|
||||
hp = m_rcc * ( m_rchp1[_chnl] + in - m_rclast1[_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_rcb + m_rcbp1[_chnl] * m_rca;
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_rclast1[_chnl] = in;
|
||||
m_rclp1[_chnl] = lp;
|
||||
m_rcbp1[_chnl] = bp;
|
||||
m_rchp1[_chnl] = hp;
|
||||
}
|
||||
return lp;
|
||||
break;
|
||||
}
|
||||
case Highpass_RC24:
|
||||
case Bandpass_RC24:
|
||||
{
|
||||
sample_t hp, bp, in;
|
||||
for( int n = 4; n != 0; --n )
|
||||
{
|
||||
// first stage is as for the 12dB case...
|
||||
in = _in0 + m_rcbp0[_chnl] * m_rcq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_rclast0[_chnl] = in;
|
||||
m_rchp0[_chnl] = hp;
|
||||
m_rcbp0[_chnl] = bp;
|
||||
|
||||
// second stage gets the output of the first stage as input...
|
||||
if( m_type == Lowpass_RC24 )
|
||||
{
|
||||
in = lp + m_rcbp1[_chnl] * m_rcq;
|
||||
}
|
||||
else if( m_type == Bandpass_RC24 )
|
||||
{
|
||||
in = bp + m_rcbp1[_chnl] * m_rcq;
|
||||
}
|
||||
else
|
||||
{
|
||||
in = hp + m_rcbp1[_chnl] * m_rcq;
|
||||
}
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
in = m_type == Highpass_RC24
|
||||
? hp + m_rcbp1[_chnl] * m_rcq
|
||||
: bp + m_rcbp1[_chnl] * m_rcq;
|
||||
|
||||
lp = in * m_rcb + m_rclp1[_chnl] * m_rca;
|
||||
lp = (lp > +1.f) ? +1.f : lp;
|
||||
lp = (lp < -1.f) ? -1.f : lp;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_rcc * ( m_rchp1[_chnl] + in - m_rclast1[_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_rcb + m_rcbp1[_chnl] * m_rca;
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_rclast1[_chnl] = in;
|
||||
m_rclp1[_chnl] = lp;
|
||||
m_rchp1[_chnl] = hp;
|
||||
m_rcbp1[_chnl] = bp;
|
||||
}
|
||||
|
||||
// output is second stage-lowpass...
|
||||
if( m_type == Lowpass_RC24 )
|
||||
{
|
||||
out = lp;
|
||||
}
|
||||
else if( m_type == Bandpass_RC24 )
|
||||
{
|
||||
out = bp;
|
||||
}
|
||||
else
|
||||
{
|
||||
out = hp;
|
||||
}
|
||||
|
||||
return out;
|
||||
return m_type == Highpass_RC24 ? hp : bp;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -317,116 +323,97 @@ public:
|
||||
{
|
||||
sample_t hp, bp, in;
|
||||
|
||||
out = 0;
|
||||
for(int o=0; o<4; o++)
|
||||
{
|
||||
// first formant
|
||||
in = _in0 + m_vfbp[0][_chnl] * m_vfq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
out = 0;
|
||||
for(int o=0; o<4; o++)
|
||||
{
|
||||
// first formant
|
||||
in = _in0 + m_vfbp[0][_chnl] * m_vfq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_vfc[0] * ( m_vfhp[0][_chnl] + in - m_vflast[0][_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = m_vfc[0] * ( m_vfhp[0][_chnl] + in - m_vflast[0][_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_vfb[0] + m_vfbp[0][_chnl] * m_vfa[0];
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = hp * m_vfb[0] + m_vfbp[0][_chnl] * m_vfa[0];
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_vflast[0][_chnl] = in;
|
||||
m_vfhp[0][_chnl] = hp;
|
||||
m_vfbp[0][_chnl] = bp;
|
||||
m_vflast[0][_chnl] = in;
|
||||
m_vfhp[0][_chnl] = hp;
|
||||
m_vfbp[0][_chnl] = bp;
|
||||
|
||||
in = bp + m_vfbp[2][_chnl] * m_vfq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
in = bp + m_vfbp[2][_chnl] * m_vfq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_vfc[0] * ( m_vfhp[2][_chnl] + in - m_vflast[2][_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = m_vfc[0] * ( m_vfhp[2][_chnl] + in - m_vflast[2][_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_vfb[0] + m_vfbp[2][_chnl] * m_vfa[0];
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = hp * m_vfb[0] + m_vfbp[2][_chnl] * m_vfa[0];
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_vflast[2][_chnl] = in;
|
||||
m_vfhp[2][_chnl] = hp;
|
||||
m_vfbp[2][_chnl] = bp;
|
||||
m_vflast[2][_chnl] = in;
|
||||
m_vfhp[2][_chnl] = hp;
|
||||
m_vfbp[2][_chnl] = bp;
|
||||
|
||||
in = bp + m_vfbp[4][_chnl] * m_vfq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
in = bp + m_vfbp[4][_chnl] * m_vfq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_vfc[0] * ( m_vfhp[4][_chnl] + in - m_vflast[4][_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = m_vfc[0] * ( m_vfhp[4][_chnl] + in - m_vflast[4][_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_vfb[0] + m_vfbp[4][_chnl] * m_vfa[0];
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = hp * m_vfb[0] + m_vfbp[4][_chnl] * m_vfa[0];
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_vflast[4][_chnl] = in;
|
||||
m_vfhp[4][_chnl] = hp;
|
||||
m_vfbp[4][_chnl] = bp;
|
||||
m_vflast[4][_chnl] = in;
|
||||
m_vfhp[4][_chnl] = hp;
|
||||
m_vfbp[4][_chnl] = bp;
|
||||
|
||||
out += bp;
|
||||
out += bp;
|
||||
|
||||
// second formant
|
||||
in = _in0 + m_vfbp[0][_chnl] * m_vfq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
// second formant
|
||||
in = _in0 + m_vfbp[0][_chnl] * m_vfq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_vfc[1] * ( m_vfhp[1][_chnl] + in - m_vflast[1][_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = m_vfc[1] * ( m_vfhp[1][_chnl] + in - m_vflast[1][_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_vfb[1] + m_vfbp[1][_chnl] * m_vfa[1];
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = hp * m_vfb[1] + m_vfbp[1][_chnl] * m_vfa[1];
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_vflast[1][_chnl] = in;
|
||||
m_vfhp[1][_chnl] = hp;
|
||||
m_vfbp[1][_chnl] = bp;
|
||||
m_vflast[1][_chnl] = in;
|
||||
m_vfhp[1][_chnl] = hp;
|
||||
m_vfbp[1][_chnl] = bp;
|
||||
|
||||
in = bp + m_vfbp[3][_chnl] * m_vfq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
in = bp + m_vfbp[3][_chnl] * m_vfq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_vfc[1] * ( m_vfhp[3][_chnl] + in - m_vflast[3][_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = m_vfc[1] * ( m_vfhp[3][_chnl] + in - m_vflast[3][_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_vfb[1] + m_vfbp[3][_chnl] * m_vfa[1];
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = hp * m_vfb[1] + m_vfbp[3][_chnl] * m_vfa[1];
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_vflast[3][_chnl] = in;
|
||||
m_vfhp[3][_chnl] = hp;
|
||||
m_vfbp[3][_chnl] = bp;
|
||||
m_vflast[3][_chnl] = in;
|
||||
m_vfhp[3][_chnl] = hp;
|
||||
m_vfbp[3][_chnl] = bp;
|
||||
|
||||
in = bp + m_vfbp[5][_chnl] * m_vfq;
|
||||
in = (in > +1.f) ? +1.f : in;
|
||||
in = (in < -1.f) ? -1.f : in;
|
||||
in = bp + m_vfbp[5][_chnl] * m_vfq;
|
||||
in = qBound( -1.0f, in, 1.0f );
|
||||
|
||||
hp = m_vfc[1] * ( m_vfhp[5][_chnl] + in - m_vflast[5][_chnl] );
|
||||
hp = (hp > +1.f) ? +1.f : hp;
|
||||
hp = (hp < -1.f) ? -1.f : hp;
|
||||
hp = m_vfc[1] * ( m_vfhp[5][_chnl] + in - m_vflast[5][_chnl] );
|
||||
hp = qBound( -1.0f, hp, 1.0f );
|
||||
|
||||
bp = hp * m_vfb[1] + m_vfbp[5][_chnl] * m_vfa[1];
|
||||
bp = (bp > +1.f) ? +1.f : bp;
|
||||
bp = (bp < -1.f) ? -1.f : bp;
|
||||
bp = hp * m_vfb[1] + m_vfbp[5][_chnl] * m_vfa[1];
|
||||
bp = qBound( -1.0f, bp, 1.0f );
|
||||
|
||||
m_vflast[5][_chnl] = in;
|
||||
m_vfhp[5][_chnl] = hp;
|
||||
m_vfbp[5][_chnl] = bp;
|
||||
m_vflast[5][_chnl] = in;
|
||||
m_vfhp[5][_chnl] = hp;
|
||||
m_vfbp[5][_chnl] = bp;
|
||||
|
||||
out += bp;
|
||||
}
|
||||
|
||||
return( out/2.0f );
|
||||
out += bp;
|
||||
}
|
||||
return( out/2.0f );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
// filter
|
||||
out = m_b0a0*_in0 +
|
||||
@@ -458,9 +445,6 @@ public:
|
||||
/*, const bool _q_is_bandwidth = false*/ )
|
||||
{
|
||||
// temp coef vars
|
||||
_freq = qBound(minFreq(), _freq, 20000.0f); // limit freq and q for not getting
|
||||
// bad noise out of the filter...
|
||||
|
||||
_q = qMax( _q, minQ() );
|
||||
|
||||
if( m_type == Lowpass_RC12 ||
|
||||
@@ -470,41 +454,39 @@ public:
|
||||
m_type == Bandpass_RC24 ||
|
||||
m_type == Highpass_RC24 )
|
||||
{
|
||||
if( _freq < 50.f )
|
||||
{
|
||||
_freq = 50.f;
|
||||
}
|
||||
|
||||
_freq = qBound( 50.0f, _freq, 20000.0f );
|
||||
|
||||
m_rca = 1.0f - (1.0f/(m_sampleRate*4)) / ( (1.0f/(_freq*2.0f*M_PI)) + (1.0f/(m_sampleRate*4)) );
|
||||
m_rcb = 1.0f - m_rca;
|
||||
m_rcc = (1.0f/(_freq*2.0f*M_PI)) / ( (1.0f/(_freq*2.0f*M_PI)) + (1.0f/(m_sampleRate*4)) );
|
||||
|
||||
|
||||
// Stretch Q/resonance, as self-oscillation reliably starts at a q of ~2.5 - ~2.6
|
||||
m_rcq = _q/4.f;
|
||||
m_rcq = _q * 0.25f;
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_type == Formantfilter )
|
||||
{
|
||||
_freq = qBound( minFreq(), _freq, 20000.0f ); // limit freq and q for not getting bad noise out of the filter...
|
||||
|
||||
// formats for a, e, i, o, u, a
|
||||
const float _f[5][2] = { { 1000, 1400 }, { 500, 2300 },
|
||||
static const float _f[5][2] = { { 1000, 1400 }, { 500, 2300 },
|
||||
{ 320, 3200 },
|
||||
{ 500, 1000 },
|
||||
{ 320, 800 } };
|
||||
static const float freqRatio = 4.0f / 14000.0f;
|
||||
|
||||
// Stretch Q/resonance
|
||||
m_vfq = _q/4.f;
|
||||
m_vfq = _q * 0.25f;
|
||||
|
||||
// frequency in lmms ranges from 1Hz to 14000Hz
|
||||
const int vowel = (int)( floor( _freq/14000.f * 4.f ) );
|
||||
const float fract = ( _freq/14000.f * 4.f ) -
|
||||
(float)vowel;
|
||||
const float vowelf = _freq * freqRatio;
|
||||
const int vowel = static_cast<int>( vowelf );
|
||||
const float fract = vowelf - vowel;
|
||||
|
||||
// interpolate between formant frequencies
|
||||
const float f0 = _f[vowel+0][0] * ( 1.0f - fract ) +
|
||||
_f[vowel+1][0] * ( fract );
|
||||
|
||||
const float f1 = _f[vowel+0][1] * ( 1.0f - fract ) +
|
||||
_f[vowel+1][1] * ( fract );
|
||||
// interpolate between formant frequencies
|
||||
const float f0 = linearInterpolate( _f[vowel+0][0], _f[vowel+1][0], fract );
|
||||
const float f1 = linearInterpolate( _f[vowel+0][1], _f[vowel+1][1], fract );
|
||||
|
||||
m_vfa[0] = 1.0f - (1.0f/(m_sampleRate*4)) /
|
||||
( (1.0f/(f0*2.0f*M_PI)) +
|
||||
@@ -521,10 +503,13 @@ public:
|
||||
m_vfc[1] = (1.0f/(f1*2.0f*M_PI)) /
|
||||
( (1.0f/(f1*2.0f*M_PI)) +
|
||||
(1.0f/(m_sampleRate*4)) );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if( m_type == Moog )
|
||||
{
|
||||
_freq = qBound( minFreq(), _freq, 20000.0f );
|
||||
|
||||
// [ 0 - 0.5 ]
|
||||
const float f = _freq / m_sampleRate;
|
||||
// (Empirical tunning)
|
||||
@@ -542,6 +527,7 @@ public:
|
||||
}
|
||||
|
||||
// other filters
|
||||
_freq = qBound( minFreq(), _freq, 20000.0f );
|
||||
const float omega = F_2PI * _freq / m_sampleRate;
|
||||
const float tsin = sinf( omega );
|
||||
const float tcos = cosf( omega );
|
||||
@@ -617,7 +603,7 @@ private:
|
||||
|
||||
// coeffs for formant-filters
|
||||
float m_vfa[4], m_vfb[4], m_vfc[4], m_vfq;
|
||||
|
||||
|
||||
typedef sample_t frame[CHANNELS];
|
||||
|
||||
// in/out history
|
||||
@@ -625,7 +611,7 @@ private:
|
||||
|
||||
// in/out history for moog-filter
|
||||
frame m_y1, m_y2, m_y3, m_y4, m_oldx, m_oldy1, m_oldy2, m_oldy3;
|
||||
|
||||
|
||||
// in/out history for RC-type-filters
|
||||
frame m_rcbp0, m_rclp0, m_rchp0, m_rclast0;
|
||||
frame m_rcbp1, m_rclp1, m_rchp1, m_rclast1;
|
||||
|
||||
@@ -142,7 +142,7 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf )
|
||||
if( mod##_e1 != 0.0f ) car += m_env1_buf[f] * mod##_e1; \
|
||||
if( mod##_e2 != 0.0f ) car += m_env2_buf[f] * mod##_e2; \
|
||||
if( mod##_l1 != 0.0f ) car += m_lfo1_buf[f] * mod##_l1; \
|
||||
if( mod##_l2 != 0.0f ) car += m_lfo2_buf[f] * mod##_l2;
|
||||
if( mod##_l2 != 0.0f ) car += m_lfo2_buf[f] * mod##_l2;
|
||||
|
||||
#define modulatephs( car, mod ) \
|
||||
if( mod##_e1 != 0.0f ) car += m_env1_buf[f] * mod##_e1; \
|
||||
@@ -1673,12 +1673,29 @@ MonstroView::MonstroView( Instrument * _instrument,
|
||||
m_opViewButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "opview_active" ) );
|
||||
m_opViewButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "opview_inactive" ) );
|
||||
toolTip::add( m_opViewButton, tr( "Operators view" ) );
|
||||
m_opViewButton -> setWhatsThis( tr( "The Operators view contains all the operators. These include both audible "
|
||||
"operators (oscillators) and inaudible operators, or modulators: "
|
||||
"Low-frequency oscillators and Envelopes. "
|
||||
"Knobs and other widgets in the Operators view have their own what's this -texts, "
|
||||
"so you can get more specific help for them that way. " ) );
|
||||
|
||||
pixmapButton * m_matViewButton = new pixmapButton( this, NULL );
|
||||
m_matViewButton -> move( 125,0 );
|
||||
m_matViewButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "matview_active" ) );
|
||||
m_matViewButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "matview_inactive" ) );
|
||||
toolTip::add( m_matViewButton, tr( "Matrix view" ) );
|
||||
m_matViewButton -> setWhatsThis( tr( "The Matrix view contains the modulation matrix. Here you can define "
|
||||
"the modulation relationships between the various operators: Each "
|
||||
"audible operator (oscillators 1-3) has 3-4 properties that can be "
|
||||
"modulated by any of the modulators. Using more modulations consumes "
|
||||
"more CPU power. "
|
||||
"The view is divided to modulation targets, grouped by the target oscillator. "
|
||||
"Available targets are volume, pitch, phase, pulse width and sub-osc ratio. "
|
||||
"Note: some targets are specific to one oscillator only. "
|
||||
"Each modulation target has 4 knobs, one for each modulator. By default "
|
||||
"the knobs are at 0, which means no modulation. Turning a knob to 1 causes "
|
||||
"that modulator to affect the modulation target as much as possible. Turning "
|
||||
"it to -1 does the same, but the modulation is inversed. " ) );
|
||||
|
||||
m_selectedViewGroup = new automatableButtonGroup( this );
|
||||
m_selectedViewGroup -> addButton( m_opViewButton );
|
||||
@@ -1850,7 +1867,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent )
|
||||
|
||||
makeknob( m_osc1VolKnob, KNOBCOL1, O1ROW, "Volume", "%", "osc1Knob" )
|
||||
makeknob( m_osc1PanKnob, KNOBCOL2, O1ROW, "Panning", "", "osc1Knob" )
|
||||
makeknob( m_osc1CrsKnob, KNOBCOL3, O1ROW, "Coarse detune", " seminotes", "osc1Knob" )
|
||||
makeknob( m_osc1CrsKnob, KNOBCOL3, O1ROW, "Coarse detune", " semitones", "osc1Knob" )
|
||||
makeknob( m_osc1FtlKnob, KNOBCOL4, O1ROW, "Finetune left", " cents", "osc1Knob" )
|
||||
makeknob( m_osc1FtrKnob, KNOBCOL5, O1ROW, "Finetune right", " cents", "osc1Knob" )
|
||||
makeknob( m_osc1SpoKnob, KNOBCOL6, O1ROW, "Stereo phase offset", " deg", "osc1Knob" )
|
||||
@@ -1863,7 +1880,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent )
|
||||
|
||||
makeknob( m_osc2VolKnob, KNOBCOL1, O2ROW, "Volume", "%", "osc2Knob" )
|
||||
makeknob( m_osc2PanKnob, KNOBCOL2, O2ROW, "Panning", "", "osc2Knob" )
|
||||
makeknob( m_osc2CrsKnob, KNOBCOL3, O2ROW, "Coarse detune", " seminotes", "osc2Knob" )
|
||||
makeknob( m_osc2CrsKnob, KNOBCOL3, O2ROW, "Coarse detune", " semitones", "osc2Knob" )
|
||||
makeknob( m_osc2FtlKnob, KNOBCOL4, O2ROW, "Finetune left", " cents", "osc2Knob" )
|
||||
makeknob( m_osc2FtrKnob, KNOBCOL5, O2ROW, "Finetune right", " cents", "osc2Knob" )
|
||||
makeknob( m_osc2SpoKnob, KNOBCOL6, O2ROW, "Stereo phase offset", " deg", "osc2Knob" )
|
||||
@@ -1879,7 +1896,7 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent )
|
||||
|
||||
makeknob( m_osc3VolKnob, KNOBCOL1, O3ROW, "Volume", "%", "osc3Knob" )
|
||||
makeknob( m_osc3PanKnob, KNOBCOL2, O3ROW, "Panning", "", "osc3Knob" )
|
||||
makeknob( m_osc3CrsKnob, KNOBCOL3, O3ROW, "Coarse detune", " seminotes", "osc3Knob" )
|
||||
makeknob( m_osc3CrsKnob, KNOBCOL3, O3ROW, "Coarse detune", " semitones", "osc3Knob" )
|
||||
makeknob( m_osc3SpoKnob, KNOBCOL4, O3ROW, "Stereo phase offset", " deg", "osc3Knob" )
|
||||
makeknob( m_osc3SubKnob, KNOBCOL5, O3ROW, "Sub-osc mix", "", "osc3Knob" )
|
||||
|
||||
@@ -1957,6 +1974,130 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent )
|
||||
m_o23ModGroup-> addButton( m_fmButton );
|
||||
m_o23ModGroup-> addButton( m_pmButton );
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// //
|
||||
// whatsthis-information strings //
|
||||
// //
|
||||
////////////////////////////////////
|
||||
|
||||
m_osc1CrsKnob -> setWhatsThis( tr( "The CRS knob changes the tuning of oscillator 1 in semitone steps. " ) );
|
||||
m_osc2CrsKnob -> setWhatsThis( tr( "The CRS knob changes the tuning of oscillator 2 in semitone steps. " ) );
|
||||
m_osc3CrsKnob -> setWhatsThis( tr( "The CRS knob changes the tuning of oscillator 3 in semitone steps. " ) );
|
||||
m_osc1FtlKnob -> setWhatsThis( tr( "FTL and FTR change the finetuning of the oscillator for left and right "
|
||||
"channels respectively. These can add stereo-detuning to the oscillator "
|
||||
"which widens the stereo image and causes an illusion of space. " ) );
|
||||
m_osc1FtrKnob -> setWhatsThis( tr( "FTL and FTR change the finetuning of the oscillator for left and right "
|
||||
"channels respectively. These can add stereo-detuning to the oscillator "
|
||||
"which widens the stereo image and causes an illusion of space. " ) );
|
||||
m_osc2FtlKnob -> setWhatsThis( tr( "FTL and FTR change the finetuning of the oscillator for left and right "
|
||||
"channels respectively. These can add stereo-detuning to the oscillator "
|
||||
"which widens the stereo image and causes an illusion of space. " ) );
|
||||
m_osc2FtrKnob -> setWhatsThis( tr( "FTL and FTR change the finetuning of the oscillator for left and right "
|
||||
"channels respectively. These can add stereo-detuning to the oscillator "
|
||||
"which widens the stereo image and causes an illusion of space. " ) );
|
||||
m_osc1SpoKnob -> setWhatsThis( tr( "The SPO knob modifies the difference in phase between left and right "
|
||||
"channels. Higher difference creates a wider stereo image. " ) );
|
||||
m_osc2SpoKnob -> setWhatsThis( tr( "The SPO knob modifies the difference in phase between left and right "
|
||||
"channels. Higher difference creates a wider stereo image. " ) );
|
||||
m_osc3SpoKnob -> setWhatsThis( tr( "The SPO knob modifies the difference in phase between left and right "
|
||||
"channels. Higher difference creates a wider stereo image. " ) );
|
||||
m_osc1PwKnob -> setWhatsThis( tr( "The PW knob controls the pulse width, also known as duty cycle, "
|
||||
"of oscillator 1. Oscillator 1 is a digital pulse wave oscillator, "
|
||||
"it doesn't produce bandlimited output, which means that you can "
|
||||
"use it as an audible oscillator but it will cause aliasing. You can "
|
||||
"also use it as an inaudible source of a sync signal, which can be "
|
||||
"used to synchronize oscillators 2 and 3. " ) );
|
||||
m_osc1SSRButton -> setWhatsThis( tr( "Send Sync on Rise: When enabled, the Sync signal is sent every time "
|
||||
"the state of oscillator 1 changes from low to high, ie. when the amplitude "
|
||||
"changes from -1 to 1. "
|
||||
"Oscillator 1's pitch, phase and pulse width may affect the timing of syncs, "
|
||||
"but its volume has no effect on them. Sync signals are sent independently "
|
||||
"for both left and right channels. " ) );
|
||||
m_osc1SSFButton -> setWhatsThis( tr( "Send Sync on Fall: When enabled, the Sync signal is sent every time "
|
||||
"the state of oscillator 1 changes from high to low, ie. when the amplitude "
|
||||
"changes from 1 to -1. "
|
||||
"Oscillator 1's pitch, phase and pulse width may affect the timing of syncs, "
|
||||
"but its volume has no effect on them. Sync signals are sent independently "
|
||||
"for both left and right channels. " ) );
|
||||
m_osc2SyncHButton -> setWhatsThis( tr( "Hard sync: Every time the oscillator receives a sync signal from oscillator 1, "
|
||||
"its phase is reset to 0 + whatever its phase offset is. " ) );
|
||||
m_osc3SyncHButton -> setWhatsThis( tr( "Hard sync: Every time the oscillator receives a sync signal from oscillator 1, "
|
||||
"its phase is reset to 0 + whatever its phase offset is. " ) );
|
||||
m_osc2SyncRButton -> setWhatsThis( tr( "Reverse sync: Every time the oscillator receives a sync signal from oscillator 1, "
|
||||
"the amplitude of the oscillator gets inverted. " ) );
|
||||
m_osc3SyncRButton -> setWhatsThis( tr( "Reverse sync: Every time the oscillator receives a sync signal from oscillator 1, "
|
||||
"the amplitude of the oscillator gets inverted. " ) );
|
||||
m_osc2WaveBox -> setWhatsThis( tr( "Choose waveform for oscillator 2. " ) );
|
||||
m_osc3Wave1Box -> setWhatsThis( tr( "Choose waveform for oscillator 3's first sub-osc. "
|
||||
"Oscillator 3 can smoothly interpolate between two different waveforms. " ) );
|
||||
m_osc3Wave2Box -> setWhatsThis( tr( "Choose waveform for oscillator 3's second sub-osc. "
|
||||
"Oscillator 3 can smoothly interpolate between two different waveforms. " ) );
|
||||
m_osc3SubKnob -> setWhatsThis( tr( "The SUB knob changes the mixing ratio of the two sub-oscs of oscillator 3. "
|
||||
"Each sub-osc can be set to produce a different waveform, and oscillator 3 "
|
||||
"can smoothly interpolate between them. All incoming modulations to oscillator 3 are applied "
|
||||
"to both sub-oscs/waveforms in the exact same way. " ) );
|
||||
m_mixButton -> setWhatsThis( tr( "In addition to dedicated modulators, Monstro allows oscillator 3 to be modulated by "
|
||||
"the output of oscillator 2. "
|
||||
"Mix mode means no modulation: the outputs of the oscillators are simply mixed together. " ) );
|
||||
m_amButton -> setWhatsThis( tr( "In addition to dedicated modulators, Monstro allows oscillator 3 to be modulated by "
|
||||
"the output of oscillator 2. "
|
||||
"AM means amplitude modulation: Oscillator 3's amplitude (volume) is modulated by oscillator 2. " ) );
|
||||
m_fmButton -> setWhatsThis( tr( "In addition to dedicated modulators, Monstro allows oscillator 3 to be modulated by "
|
||||
"the output of oscillator 2. "
|
||||
"FM means frequency modulation: Oscillator 3's frequency (pitch) is modulated by oscillator 2. "
|
||||
"The frequency modulation is implemented as phase modulation, which gives a more stable overall pitch "
|
||||
"than \"pure\" frequency modulation. " ) );
|
||||
m_pmButton -> setWhatsThis( tr( "In addition to dedicated modulators, Monstro allows oscillator 3 to be modulated by "
|
||||
"the output of oscillator 2. "
|
||||
"PM means phase modulation: Oscillator 3's phase is modulated by oscillator 2. "
|
||||
"It differs from frequency modulation in that the phase changes are not cumulative. " ) );
|
||||
m_lfo1WaveBox -> setWhatsThis( tr( "Select the waveform for LFO 1. "
|
||||
"\"Random\" and \"Random smooth\" are special waveforms: "
|
||||
"they produce random output, where the rate of the LFO controls how often "
|
||||
"the state of the LFO changes. The smooth version interpolates between these "
|
||||
"states with cosine interpolation. These random modes can be used to give "
|
||||
"\"life\" to your presets - add some of that analog unpredictability... " ) );
|
||||
m_lfo2WaveBox -> setWhatsThis( tr( "Select the waveform for LFO 2. "
|
||||
"\"Random\" and \"Random smooth\" are special waveforms: "
|
||||
"they produce random output, where the rate of the LFO controls how often "
|
||||
"the state of the LFO changes. The smooth version interpolates between these "
|
||||
"states with cosine interpolation. These random modes can be used to give "
|
||||
"\"life\" to your presets - add some of that analog unpredictability... " ) );
|
||||
m_lfo1AttKnob -> setWhatsThis( tr( "Attack causes the LFO to come on gradually from the start of the note. " ) );
|
||||
m_lfo2AttKnob -> setWhatsThis( tr( "Attack causes the LFO to come on gradually from the start of the note. " ) );
|
||||
m_lfo1RateKnob -> setWhatsThis( tr( "Rate sets the speed of the LFO, measured in milliseconds per cycle. Can be synced to tempo. " ) );
|
||||
m_lfo2RateKnob -> setWhatsThis( tr( "Rate sets the speed of the LFO, measured in milliseconds per cycle. Can be synced to tempo. " ) );
|
||||
m_lfo1PhsKnob -> setWhatsThis( tr( "PHS controls the phase offset of the LFO. " ) );
|
||||
m_lfo2PhsKnob -> setWhatsThis( tr( "PHS controls the phase offset of the LFO. " ) );
|
||||
|
||||
m_env1PreKnob -> setWhatsThis( tr( "PRE, or pre-delay, delays the start of the envelope from the start of the note. 0 means no delay. " ) );
|
||||
m_env2PreKnob -> setWhatsThis( tr( "PRE, or pre-delay, delays the start of the envelope from the start of the note. 0 means no delay. " ) );
|
||||
m_env1AttKnob -> setWhatsThis( tr( "ATT, or attack, controls how fast the envelope ramps up at start, measured in milliseconds. "
|
||||
"A value of 0 means instant. " ) );
|
||||
m_env2AttKnob -> setWhatsThis( tr( "ATT, or attack, controls how fast the envelope ramps up at start, measured in milliseconds. "
|
||||
"A value of 0 means instant. " ) );
|
||||
m_env1HoldKnob -> setWhatsThis( tr( "HOLD controls how long the envelope stays at peak after the attack phase. " ) );
|
||||
m_env2HoldKnob -> setWhatsThis( tr( "HOLD controls how long the envelope stays at peak after the attack phase. " ) );
|
||||
m_env1DecKnob -> setWhatsThis( tr( "DEC, or decay, controls how fast the envelope falls off from its peak, measured in milliseconds "
|
||||
"it would take to go from peak to zero. The actual decay may be shorter if sustain is used. ") );
|
||||
m_env2DecKnob -> setWhatsThis( tr( "DEC, or decay, controls how fast the envelope falls off from its peak, measured in milliseconds "
|
||||
"it would take to go from peak to zero. The actual decay may be shorter if sustain is used. ") );
|
||||
m_env1SusKnob -> setWhatsThis( tr( "SUS, or sustain, controls the sustain level of the envelope. The decay phase will not go below this level "
|
||||
"as long as the note is held. " ) );
|
||||
m_env2SusKnob -> setWhatsThis( tr( "SUS, or sustain, controls the sustain level of the envelope. The decay phase will not go below this level "
|
||||
"as long as the note is held. " ) );
|
||||
m_env1RelKnob -> setWhatsThis( tr( "REL, or release, controls how long the release is for the note, measured in how long it would take to "
|
||||
"fall from peak to zero. Actual release may be shorter, depending on at what phase the note is released. ") );
|
||||
m_env2RelKnob -> setWhatsThis( tr( "REL, or release, controls how long the release is for the note, measured in how long it would take to "
|
||||
"fall from peak to zero. Actual release may be shorter, depending on at what phase the note is released. ") );
|
||||
m_env1SlopeKnob -> setWhatsThis( tr( "The slope knob controls the curve or shape of the envelope. A value of 0 creates straight rises and falls. "
|
||||
"Negative values create curves that start slowly, peak quickly and fall of slowly again. "
|
||||
"Positive values create curves that start and end quickly, and stay longer near the peaks. " ) );
|
||||
m_env2SlopeKnob -> setWhatsThis( tr( "The slope knob controls the curve or shape of the envelope. A value of 0 creates straight rises and falls. "
|
||||
"Negative values create curves that start slowly, peak quickly and fall of slowly again. "
|
||||
"Positive values create curves that start and end quickly, and stay longer near the peaks. " ) );
|
||||
return( view );
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 367 B After Width: | Height: | Size: 522 B |
|
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 599 B |
|
Before Width: | Height: | Size: 478 B After Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 500 B |
|
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 529 B |
|
Before Width: | Height: | Size: 398 B After Width: | Height: | Size: 578 B |
|
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 468 B |
|
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 427 B |
|
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 539 B |
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 602 B |
@@ -34,6 +34,34 @@
|
||||
#include "bb_track_container.h"
|
||||
#include "ValueBuffer.h"
|
||||
|
||||
FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) :
|
||||
m_from( from ),
|
||||
m_to( to )
|
||||
{
|
||||
//qDebug( "created: %d to %d", m_from->m_channelIndex, m_to->m_channelIndex );
|
||||
// create send amount model
|
||||
m_amount = new FloatModel( amount, 0, 1, 0.001, NULL,
|
||||
tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) );
|
||||
}
|
||||
|
||||
|
||||
FxRoute::~FxRoute()
|
||||
{
|
||||
// remove send model
|
||||
delete m_amount;
|
||||
}
|
||||
|
||||
|
||||
void FxRoute::updateName()
|
||||
{
|
||||
if( m_amount)
|
||||
{
|
||||
m_amount->setDisplayName(
|
||||
tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FxChannel::FxChannel( int idx, Model * _parent ) :
|
||||
m_fxChain( NULL ),
|
||||
m_hasInput( false ),
|
||||
@@ -65,7 +93,6 @@ FxChannel::~FxChannel()
|
||||
|
||||
void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
{
|
||||
FxMixer * fxm = engine::fxMixer();
|
||||
const fpp_t fpp = engine::mixer()->framesPerPeriod();
|
||||
|
||||
// <tobydox> ignore the passed _buf
|
||||
@@ -77,17 +104,15 @@ void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
// <tobydox> this improves cache hit rate
|
||||
_buf = m_buffer;
|
||||
|
||||
// SMF: OK, due to the fact, that the data from the audio-tracks has been
|
||||
// written into our buffer already, all which needs to be done at this
|
||||
// stage is to process inter-channel sends.
|
||||
|
||||
if( m_muteModel.value() == false )
|
||||
{
|
||||
// OK, we are not muted, so we go recursively through all the channels
|
||||
// which send to us (our children)...
|
||||
foreach( fx_ch_t senderIndex, m_receives )
|
||||
foreach( FxRoute * senderRoute, m_receives )
|
||||
{
|
||||
FxChannel * sender = fxm->effectChannel( senderIndex );
|
||||
FxChannel * sender = senderRoute->sender();
|
||||
FloatModel * sendModel = senderRoute->amount();
|
||||
if( ! sendModel ) qFatal( "Error: no send model found from %d to %d", senderRoute->senderIndex(), m_channelIndex );
|
||||
|
||||
// wait for the sender job - either it's just been queued yet,
|
||||
// then ThreadableJob::process() will process it now within this
|
||||
@@ -101,8 +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 = fxm->channelSendModel( senderIndex, m_channelIndex )->hasSampleExactData()
|
||||
? fxm->channelSendModel( senderIndex, m_channelIndex )->valueBuffer()
|
||||
ValueBuffer * sendBuf = sendModel->hasSampleExactData()
|
||||
? sendModel->valueBuffer()
|
||||
: NULL;
|
||||
ValueBuffer * volBuf = sender->m_volumeModel.hasSampleExactData()
|
||||
? sender->m_volumeModel.valueBuffer()
|
||||
@@ -114,7 +139,7 @@ void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
// use sample-exact mixing if sample-exact values are available
|
||||
if( ! volBuf && ! sendBuf ) // neither volume nor send has sample-exact data...
|
||||
{
|
||||
const float v = sender->m_volumeModel.value() * fxm->channelSendModel( senderIndex, m_channelIndex )->value();
|
||||
const float v = sender->m_volumeModel.value() * sendModel->value();
|
||||
MixHelpers::addMultiplied( _buf, ch_buf, v, fpp );
|
||||
}
|
||||
else if( volBuf && sendBuf ) // both volume and send have sample-exact data
|
||||
@@ -123,7 +148,7 @@ void FxChannel::doProcessing( sampleFrame * _buf )
|
||||
}
|
||||
else if( volBuf ) // volume has sample-exact data but send does not
|
||||
{
|
||||
const float v = fxm->channelSendModel( senderIndex, m_channelIndex )->value();
|
||||
const float v = sendModel->value();
|
||||
MixHelpers::addMultipliedByBuffer( _buf, ch_buf, v, volBuf, fpp );
|
||||
}
|
||||
else // vice versa
|
||||
@@ -168,12 +193,12 @@ FxMixer::FxMixer() :
|
||||
|
||||
FxMixer::~FxMixer()
|
||||
{
|
||||
while( ! m_fxRoutes.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( m_fxRoutes.first() );
|
||||
}
|
||||
for( int i = 0; i < m_fxChannels.size(); ++i )
|
||||
{
|
||||
for( int j = 0; j < m_fxChannels[i]->m_sendAmount.size(); ++j)
|
||||
{
|
||||
delete m_fxChannels[i]->m_sendAmount[j];
|
||||
}
|
||||
delete m_fxChannels[i];
|
||||
}
|
||||
}
|
||||
@@ -193,10 +218,12 @@ int FxMixer::createChannel()
|
||||
}
|
||||
|
||||
|
||||
void FxMixer::deleteChannel(int index)
|
||||
void FxMixer::deleteChannel( int index )
|
||||
{
|
||||
m_fxChannels[index]->m_lock.lock();
|
||||
|
||||
FxChannel * ch = m_fxChannels[index];
|
||||
|
||||
// go through every instrument and adjust for the channel index change
|
||||
TrackContainer::TrackList tracks;
|
||||
tracks += engine::getSong()->tracks();
|
||||
@@ -223,52 +250,34 @@ void FxMixer::deleteChannel(int index)
|
||||
}
|
||||
|
||||
// delete all of this channel's sends and receives
|
||||
while( ! m_fxChannels[index]->m_sends.isEmpty() )
|
||||
while( ! ch->m_sends.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( index, m_fxChannels[index]->m_sends.first() );
|
||||
deleteChannelSend( ch->m_sends.first() );
|
||||
}
|
||||
while( ! m_fxChannels[index]->m_receives.isEmpty() )
|
||||
while( ! ch->m_receives.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( m_fxChannels[index]->m_receives.first(), index );
|
||||
}
|
||||
|
||||
for(int i=0; i<m_fxChannels.size(); ++i)
|
||||
{
|
||||
// for every send/receive, adjust for the channel index change
|
||||
for(int j=0; j<m_fxChannels[i]->m_sends.size(); ++j)
|
||||
{
|
||||
if( m_fxChannels[i]->m_sends[j] > index )
|
||||
{
|
||||
// subtract 1 to make up for the missing channel
|
||||
--m_fxChannels[i]->m_sends[j];
|
||||
}
|
||||
}
|
||||
for(int j=0; j<m_fxChannels[i]->m_receives.size(); ++j)
|
||||
{
|
||||
if( m_fxChannels[i]->m_receives[j] > index )
|
||||
{
|
||||
// subtract 1 to make up for the missing channel
|
||||
--m_fxChannels[i]->m_receives[j];
|
||||
}
|
||||
}
|
||||
|
||||
deleteChannelSend( ch->m_receives.first() );
|
||||
}
|
||||
|
||||
// actually delete the channel
|
||||
delete m_fxChannels[index];
|
||||
m_fxChannels.remove(index);
|
||||
|
||||
for( int i = index; i < m_fxChannels.size(); ++i )
|
||||
{
|
||||
validateChannelName( i, i + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixer::moveChannelLeft(int index)
|
||||
void FxMixer::moveChannelLeft( int index )
|
||||
{
|
||||
// can't move master or first channel
|
||||
if( index <= 1 || index >= m_fxChannels.size() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_sendsMutex.lock();
|
||||
// channels to swap
|
||||
int a = index - 1, b = index;
|
||||
|
||||
@@ -299,122 +308,132 @@ void FxMixer::moveChannelLeft(int index)
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<m_fxChannels.size(); ++i)
|
||||
{
|
||||
// for every send/receive, adjust for the channel index change
|
||||
for(int j=0; j<m_fxChannels[i]->m_sends.size(); ++j)
|
||||
{
|
||||
if( m_fxChannels[i]->m_sends[j] == a )
|
||||
{
|
||||
m_fxChannels[i]->m_sends[j] = b;
|
||||
}
|
||||
else if( m_fxChannels[i]->m_sends[j] == b )
|
||||
{
|
||||
m_fxChannels[i]->m_sends[j] = a;
|
||||
}
|
||||
}
|
||||
for(int j=0; j<m_fxChannels[i]->m_receives.size(); ++j)
|
||||
{
|
||||
if( m_fxChannels[i]->m_receives[j] == a )
|
||||
{
|
||||
m_fxChannels[i]->m_receives[j] = b;
|
||||
}
|
||||
else if( m_fxChannels[i]->m_receives[j] == b )
|
||||
{
|
||||
m_fxChannels[i]->m_receives[j] = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// actually do the swap
|
||||
FxChannel * tmpChannel = m_fxChannels[a];
|
||||
m_fxChannels[a] = m_fxChannels[b];
|
||||
m_fxChannels[b] = tmpChannel;
|
||||
m_sendsMutex.unlock();
|
||||
|
||||
validateChannelName( a, b );
|
||||
validateChannelName( b, a );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixer::moveChannelRight(int index)
|
||||
void FxMixer::moveChannelRight( int index )
|
||||
{
|
||||
moveChannelLeft(index+1);
|
||||
moveChannelLeft( index + 1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel,
|
||||
float amount)
|
||||
void FxMixer::createChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel,
|
||||
float amount )
|
||||
{
|
||||
// qDebug( "requested: %d to %d", fromChannel, toChannel );
|
||||
// find the existing connection
|
||||
FxChannel * from = m_fxChannels[fromChannel];
|
||||
for(int i=0; i<from->m_sends.size(); ++i){
|
||||
if( from->m_sends[i] == toChannel )
|
||||
FxChannel * to = m_fxChannels[toChannel];
|
||||
|
||||
for( int i=0; i<from->m_sends.size(); ++i )
|
||||
{
|
||||
if( from->m_sends[i]->receiver() == to )
|
||||
{
|
||||
// simply adjust the amount
|
||||
from->m_sendAmount[i]->setValue(amount);
|
||||
from->m_sends[i]->amount()->setValue( amount );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// connection does not exist. create a new one
|
||||
m_sendsMutex.lock();
|
||||
// add to from's sends
|
||||
from->m_sends.push_back(toChannel);
|
||||
from->m_sendAmount.push_back(new FloatModel(amount, 0, 1, 0.001, NULL,
|
||||
tr("Amount to send")));
|
||||
createRoute( from, to, amount );
|
||||
}
|
||||
|
||||
// add to to's receives
|
||||
m_fxChannels[toChannel]->m_receives.push_back(fromChannel);
|
||||
|
||||
void FxMixer::createRoute( FxChannel * from, FxChannel * to, float amount )
|
||||
{
|
||||
if( from == to )
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_sendsMutex.lock();
|
||||
FxRoute * route = new FxRoute( from, to, amount );
|
||||
|
||||
// add us to from's sends
|
||||
from->m_sends.append( route );
|
||||
|
||||
// add us to to's receives
|
||||
to->m_receives.append( route );
|
||||
|
||||
// add us to fxmixer's list
|
||||
engine::fxMixer()->m_fxRoutes.append( route );
|
||||
m_sendsMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// delete the connection made by createChannelSend
|
||||
void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel)
|
||||
void FxMixer::deleteChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel )
|
||||
{
|
||||
// delete the send
|
||||
FxChannel * from = m_fxChannels[fromChannel];
|
||||
FxChannel * to = m_fxChannels[toChannel];
|
||||
m_sendsMutex.lock();
|
||||
// find and delete the send entry
|
||||
for(int i=0; i<from->m_sends.size(); ++i) {
|
||||
if( from->m_sends[i] == toChannel )
|
||||
{
|
||||
// delete this index
|
||||
delete from->m_sendAmount[i];
|
||||
from->m_sendAmount.remove(i);
|
||||
from->m_sends.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// find and delete the receive entry
|
||||
for(int i=0; i<to->m_receives.size(); ++i)
|
||||
// find and delete the send entry
|
||||
for( int i = 0; i < from->m_sends.size(); ++i )
|
||||
{
|
||||
if( to->m_receives[i] == fromChannel )
|
||||
if( from->m_sends[i]->receiver() == to )
|
||||
{
|
||||
// delete this index
|
||||
to->m_receives.remove(i);
|
||||
deleteChannelSend( from->m_sends[i] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FxMixer::deleteChannelSend( FxRoute * route )
|
||||
{
|
||||
m_sendsMutex.lock();
|
||||
// remove us from from's sends
|
||||
route->sender()->m_sends.remove( route->sender()->m_sends.indexOf( route ) );
|
||||
// remove us from to's receives
|
||||
route->receiver()->m_receives.remove( route->receiver()->m_receives.indexOf( route ) );
|
||||
// remove us from fxmixer's list
|
||||
engine::fxMixer()->m_fxRoutes.remove( engine::fxMixer()->m_fxRoutes.indexOf( route ) );
|
||||
delete route;
|
||||
m_sendsMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
bool FxMixer::isInfiniteLoop(fx_ch_t sendFrom, fx_ch_t sendTo) {
|
||||
bool FxMixer::isInfiniteLoop( fx_ch_t sendFrom, fx_ch_t sendTo )
|
||||
{
|
||||
if( sendFrom == sendTo ) return true;
|
||||
//m_sendsMutex.lock();
|
||||
FxChannel * from = m_fxChannels[sendFrom];
|
||||
FxChannel * to = m_fxChannels[sendTo];
|
||||
bool b = checkInfiniteLoop( from, to );
|
||||
//m_sendsMutex.unlock();
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
bool FxMixer::checkInfiniteLoop( FxChannel * from, FxChannel * to )
|
||||
{
|
||||
// can't send master to anything
|
||||
if( sendFrom == 0 ) return true;
|
||||
if( from == m_fxChannels[0] )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// can't send channel to itself
|
||||
if( sendFrom == sendTo ) return true;
|
||||
if( from == to )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// follow sendTo's outputs recursively looking for something that sends
|
||||
// to sendFrom
|
||||
for(int i=0; i<m_fxChannels[sendTo]->m_sends.size(); ++i)
|
||||
for( int i=0; i < to->m_sends.size(); ++i )
|
||||
{
|
||||
if( isInfiniteLoop( sendFrom, m_fxChannels[sendTo]->m_sends[i] ) )
|
||||
if( checkInfiniteLoop( from, to->m_sends[i]->receiver() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -425,13 +444,23 @@ bool FxMixer::isInfiniteLoop(fx_ch_t sendFrom, fx_ch_t sendTo) {
|
||||
|
||||
|
||||
// how much does fromChannel send its output to the input of toChannel?
|
||||
FloatModel * FxMixer::channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel)
|
||||
FloatModel * FxMixer::channelSendModel( fx_ch_t fromChannel, fx_ch_t toChannel )
|
||||
{
|
||||
FxChannel * from = m_fxChannels[fromChannel];
|
||||
for(int i=0; i<from->m_sends.size(); ++i){
|
||||
if( from->m_sends[i] == toChannel )
|
||||
return from->m_sendAmount[i];
|
||||
if( fromChannel == toChannel )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
FxChannel * from = m_fxChannels[fromChannel];
|
||||
FxChannel * to = m_fxChannels[toChannel];
|
||||
|
||||
foreach( FxRoute * route, from->m_sends )
|
||||
{
|
||||
if( route->receiver() == to )
|
||||
{
|
||||
return route->amount();
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -439,15 +468,6 @@ FloatModel * FxMixer::channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel)
|
||||
|
||||
void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
|
||||
{
|
||||
// SMF: it seems like here the track-channels are mixed in... but from where
|
||||
// is this called and when and why...?!?
|
||||
//
|
||||
// OK, found it (git grep is your friend...): This is the next part,
|
||||
// where there is a mix between push and pull model inside the core, as
|
||||
// the audio-tracks *push* their data into the fx-channels hopefully just
|
||||
// before the Mixer-Channels are processed... Sorry to say this: but this
|
||||
// took me senseless hours to find out and is silly, too...
|
||||
|
||||
if( m_fxChannels[_ch]->m_muteModel.value() == false )
|
||||
{
|
||||
m_fxChannels[_ch]->m_lock.lock();
|
||||
@@ -468,24 +488,22 @@ void FxMixer::prepareMasterMix()
|
||||
|
||||
|
||||
|
||||
void FxMixer::addChannelLeaf( int _ch, sampleFrame * _buf )
|
||||
void FxMixer::addChannelLeaf( FxChannel * ch, sampleFrame * buf )
|
||||
{
|
||||
FxChannel * thisCh = m_fxChannels[_ch];
|
||||
|
||||
// if we're muted or this channel is seen already, discount it
|
||||
if( thisCh->m_queued )
|
||||
if( ch->m_queued )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( const int senderIndex, thisCh->m_receives )
|
||||
foreach( FxRoute * senderRoute, ch->m_receives )
|
||||
{
|
||||
addChannelLeaf( senderIndex, _buf );
|
||||
addChannelLeaf( senderRoute->sender(), buf );
|
||||
}
|
||||
|
||||
// add this channel to job list
|
||||
thisCh->m_queued = true;
|
||||
MixerWorkerThread::addJob( thisCh );
|
||||
ch->m_queued = true;
|
||||
MixerWorkerThread::addJob( ch );
|
||||
}
|
||||
|
||||
|
||||
@@ -498,15 +516,14 @@ void FxMixer::masterMix( sampleFrame * _buf )
|
||||
// and add all channels to job list that have no dependencies
|
||||
// when the channel completes it will check its parent to see if it needs
|
||||
// to be processed.
|
||||
m_sendsMutex.lock();
|
||||
//m_sendsMutex.lock();
|
||||
MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
|
||||
addChannelLeaf( 0, _buf );
|
||||
addChannelLeaf( m_fxChannels[0], _buf );
|
||||
while( m_fxChannels[0]->state() != ThreadableJob::Done )
|
||||
{
|
||||
MixerWorkerThread::startAndWaitForJobs();
|
||||
}
|
||||
//m_fxChannels[0]->doProcessing( NULL );
|
||||
m_sendsMutex.unlock();
|
||||
//m_sendsMutex.unlock();
|
||||
|
||||
// handle sample-exact data in master volume fader
|
||||
ValueBuffer * volBuf = m_fxChannels[0]->m_volumeModel.hasSampleExactData()
|
||||
@@ -564,7 +581,7 @@ void FxMixer::clearChannel(fx_ch_t index)
|
||||
ch->m_volumeModel.setValue( 1.0f );
|
||||
ch->m_muteModel.setValue( false );
|
||||
ch->m_name = ( index == 0 ) ? tr( "Master" ) : tr( "FX %1" ).arg( index );
|
||||
ch->m_volumeModel.setDisplayName(ch->m_name );
|
||||
ch->m_volumeModel.setDisplayName( ch->m_name );
|
||||
|
||||
// send only to master
|
||||
if( index > 0)
|
||||
@@ -572,17 +589,17 @@ void FxMixer::clearChannel(fx_ch_t index)
|
||||
// delete existing sends
|
||||
while( ! ch->m_sends.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( index, ch->m_sends.first() );
|
||||
deleteChannelSend( ch->m_sends.first() );
|
||||
}
|
||||
|
||||
// add send to master
|
||||
createChannelSend(index, 0);
|
||||
createChannelSend( index, 0 );
|
||||
}
|
||||
|
||||
// delete receives
|
||||
while( ! ch->m_receives.isEmpty() )
|
||||
{
|
||||
deleteChannelSend( ch->m_receives.first(), index );
|
||||
deleteChannelSend( ch->m_receives.first() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,8 +624,8 @@ void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
QDomElement sendsDom = _doc.createElement( QString( "send" ) );
|
||||
fxch.appendChild( sendsDom );
|
||||
|
||||
sendsDom.setAttribute( "channel", ch->m_sends[si] );
|
||||
ch->m_sendAmount[si]->saveSettings( _doc, sendsDom, "amount");
|
||||
sendsDom.setAttribute( "channel", ch->m_sends[si]->receiverIndex() );
|
||||
ch->m_sends[si]->amount()->saveSettings( _doc, sendsDom, "amount" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -621,7 +638,7 @@ void FxMixer::allocateChannelsTo(int num)
|
||||
createChannel();
|
||||
|
||||
// delete the default send to master
|
||||
deleteChannelSend(m_fxChannels.size()-1, 0);
|
||||
deleteChannelSend( m_fxChannels.size()-1, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,7 +675,7 @@ void FxMixer::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
thereIsASend = true;
|
||||
int sendTo = chDataItem.attribute( "channel" ).toInt();
|
||||
allocateChannelsTo( sendTo) ;
|
||||
allocateChannelsTo( sendTo ) ;
|
||||
float amount = chDataItem.attribute( "amount" ).toFloat();
|
||||
createChannelSend( num, sendTo, amount );
|
||||
}
|
||||
@@ -674,10 +691,31 @@ void FxMixer::loadSettings( const QDomElement & _this )
|
||||
// create a send from every channel into master
|
||||
for( int i=1; i<m_fxChannels.size(); ++i )
|
||||
{
|
||||
createChannelSend(i, 0);
|
||||
createChannelSend( i, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
|
||||
void FxMixer::validateChannelName( int index, int oldIndex )
|
||||
{
|
||||
FxChannel * fxc = m_fxChannels[ index ];
|
||||
if( fxc->m_name == tr( "FX %1" ).arg( oldIndex ) )
|
||||
{
|
||||
fxc->m_name = tr( "FX %1" ).arg( index );
|
||||
}
|
||||
// set correct channel index
|
||||
fxc->m_channelIndex = index;
|
||||
|
||||
// now check all routes and update names of the send models
|
||||
foreach( FxRoute * r, fxc->m_sends )
|
||||
{
|
||||
r->updateName();
|
||||
}
|
||||
foreach( FxRoute * r, fxc->m_receives )
|
||||
{
|
||||
r->updateName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ FxMixerView::FxMixerView() :
|
||||
channelArea->setMinimumWidth( fxLineSize.width() * 6 );
|
||||
channelArea->setFixedHeight( fxLineSize.height() +
|
||||
style()->pixelMetric( QStyle::PM_ScrollBarExtent ) );
|
||||
ml->addWidget(channelArea);
|
||||
ml->addWidget( channelArea, 1, Qt::AlignTop );
|
||||
|
||||
// show the add new effect channel button
|
||||
QPushButton * newChannelBtn = new QPushButton( embed::getIconPixmap( "new_channel" ), QString::null, this );
|
||||
@@ -132,7 +132,7 @@ FxMixerView::FxMixerView() :
|
||||
|
||||
|
||||
// add the stacked layout for the effect racks of fx channels
|
||||
ml->addWidget( m_racksWidget, 0, Qt::AlignTop );
|
||||
ml->addWidget( m_racksWidget, 0, Qt::AlignTop | Qt::AlignRight );
|
||||
|
||||
setCurrentFxLine( m_fxChannelViews[0]->m_fxLine );
|
||||
|
||||
|
||||
@@ -40,12 +40,23 @@
|
||||
#include "caption_menu.h"
|
||||
|
||||
const int FxLine::FxLineHeight = 287;
|
||||
QPixmap * FxLine::s_sendBgArrow = NULL;
|
||||
QPixmap * FxLine::s_receiveBgArrow = NULL;
|
||||
|
||||
FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) :
|
||||
QWidget( _parent ),
|
||||
m_mv( _mv ),
|
||||
m_channelIndex( _channelIndex )
|
||||
{
|
||||
if( ! s_sendBgArrow )
|
||||
{
|
||||
s_sendBgArrow = new QPixmap( embed::getIconPixmap( "send_bg_arrow", 29, 56 ) );
|
||||
}
|
||||
if( ! s_receiveBgArrow )
|
||||
{
|
||||
s_receiveBgArrow = new QPixmap( embed::getIconPixmap( "receive_bg_arrow", 29, 56 ) );
|
||||
}
|
||||
|
||||
setFixedSize( 33, FxLineHeight );
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) );
|
||||
@@ -97,7 +108,7 @@ void FxLine::setChannelIndex(int index) {
|
||||
}
|
||||
|
||||
|
||||
static void drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name, bool isActive, bool sendToThis )
|
||||
void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name, bool isActive, bool sendToThis, bool receiveFromThis )
|
||||
{
|
||||
int width = fxLine->rect().width();
|
||||
int height = fxLine->rect().height();
|
||||
@@ -111,10 +122,7 @@ static void drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name,
|
||||
|
||||
p->fillRect( fxLine->rect(), isActive ? fxLine->backgroundActive() : p->background() );
|
||||
|
||||
p->setPen( QColor( 0, 0, 0, 75 ) );
|
||||
p->drawRect( 0, 0, width-2, height-2 );
|
||||
|
||||
p->setPen( QColor( 255, 255, 255, 75 ) );
|
||||
p->setPen( QColor( 255, 255, 255, isActive ? 100 : 50 ) );
|
||||
p->drawRect( 1, 1, width-3, height-3 );
|
||||
|
||||
p->setPen( isActive ? sh_color : QColor( 0, 0, 0, 50 ) );
|
||||
@@ -123,8 +131,11 @@ static void drawFxLine( QPainter* p, const FxLine *fxLine, const QString& name,
|
||||
// draw the mixer send background
|
||||
if( sendToThis )
|
||||
{
|
||||
p->drawPixmap( 3, 0, 28, 56,
|
||||
embed::getIconPixmap("send_bg_arrow", 28, 56 ) );
|
||||
p->drawPixmap( 2, 0, 29, 56, *s_sendBgArrow );
|
||||
}
|
||||
else if( receiveFromThis )
|
||||
{
|
||||
p->drawPixmap( 2, 0, 29, 56, *s_receiveBgArrow );
|
||||
}
|
||||
|
||||
// draw the channel name
|
||||
@@ -145,12 +156,14 @@ void FxLine::paintEvent( QPaintEvent * )
|
||||
{
|
||||
FxMixer * mix = engine::fxMixer();
|
||||
bool sendToThis = mix->channelSendModel(
|
||||
m_mv->currentFxLine()->m_channelIndex, m_channelIndex) != NULL;
|
||||
m_mv->currentFxLine()->m_channelIndex, m_channelIndex ) != NULL;
|
||||
bool receiveFromThis = mix->channelSendModel(
|
||||
m_channelIndex, m_mv->currentFxLine()->m_channelIndex ) != NULL;
|
||||
QPainter painter;
|
||||
painter.begin( this );
|
||||
drawFxLine( &painter, this,
|
||||
mix->effectChannel(m_channelIndex)->m_name,
|
||||
m_mv->currentFxLine() == this, sendToThis );
|
||||
mix->effectChannel( m_channelIndex )->m_name,
|
||||
m_mv->currentFxLine() == this, sendToThis, receiveFromThis );
|
||||
painter.end();
|
||||
}
|
||||
|
||||
|
||||