Commit c701929d authored by Jean-Baptiste Kempf's avatar Jean-Baptiste Kempf

Spatializer audio filter, work for Google Summer of Code, by Isaac Biodun...

Spatializer audio filter, work for Google Summer of Code, by Isaac Biodun Osunkunle: osunkunle --- gmail.com
This is the first part of his work.

parent b73d3144
SOURCES_spatializer = spatializer.cpp \
allpass.cpp allpass.hpp comb.cpp comb.hpp \
denormals.h tuning.h revmodel.cpp revmodel.hpp
// Allpass filter implementation
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#include "allpass.hpp"
allpass::allpass()
{
bufidx = 0;
}
void allpass::setbuffer(float *buf, int size)
{
buffer = buf;
bufsize = size;
}
void allpass::mute()
{
for (int i=0; i<bufsize; i++)
buffer[i]=0;
}
void allpass::setfeedback(float val)
{
feedback = val;
}
float allpass::getfeedback()
{
return feedback;
}
//ends
\ No newline at end of file
// Allpass filter declaration
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#ifndef _allpass_
#define _allpass_
#include "denormals.h"
class allpass
{
public:
allpass();
void setbuffer(float *buf, int size);
inline float process(float inp);
void mute();
void setfeedback(float val);
float getfeedback();
// private:
float feedback;
float *buffer;
int bufsize;
int bufidx;
};
// Big to inline - but crucial for speed
inline float allpass::process(float input)
{
float output;
float bufout;
bufout = buffer[bufidx];
undenormalise(bufout);
output = -input + bufout;
buffer[bufidx] = input + (bufout*feedback);
if(++bufidx>=bufsize) bufidx = 0;
return output;
}
#endif//_allpass
//ends
// Comb filter implementation
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#include "comb.hpp"
comb::comb()
{
filterstore = 0;
bufidx = 0;
}
void comb::setbuffer(float *buf, int size)
{
buffer = buf;
bufsize = size;
}
void comb::mute()
{
for (int i=0; i<bufsize; i++)
buffer[i]=0;
}
void comb::setdamp(float val)
{
damp1 = val;
damp2 = 1-val;
}
float comb::getdamp()
{
return damp1;
}
void comb::setfeedback(float val)
{
feedback = val;
}
float comb::getfeedback()
{
return feedback;
}
// ends
// Comb filter class declaration
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#ifndef _comb_
#define _comb_
#include "denormals.h"
class comb
{
public:
comb();
void setbuffer(float *buf, int size);
inline float process(float inp);
void mute();
void setdamp(float val);
float getdamp();
void setfeedback(float val);
float getfeedback();
private:
float feedback;
float filterstore;
float damp1;
float damp2;
float *buffer;
int bufsize;
int bufidx;
};
// Big to inline - but crucial for speed
inline float comb::process(float input)
{
float output;
output = buffer[bufidx];
undenormalise(output);
filterstore = (output*damp2) + (filterstore*damp1);
undenormalise(filterstore);
buffer[bufidx] = input + (filterstore*feedback);
if(++bufidx>=bufsize) bufidx = 0;
return output;
}
#endif //_comb_
//ends
// Macro for killing denormalled numbers
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// Based on IS_DENORMAL macro by Jon Watte
// This code is public domain
#ifndef _denormals_
#define _denormals_
#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
#endif//_denormals_
//ends
// Reverb model implementation
//
//
// Google Summer of Code 2007
//
// Authors: Biodun Osunkunle <biodun@videolan.org>
//
// Mentor : Jean-Baptiste Kempf <jb@videolan.org>
//
// Original written by Jezar at Dreampoint, June 2000
// This code is public domain
#include "revmodel.hpp"
#include <stdlib.h>
revmodel::revmodel()
{
// Tie the components to their buffers
combL[0].setbuffer(bufcombL1,combtuningL1);
combR[0].setbuffer(bufcombR1,combtuningR1);
combL[1].setbuffer(bufcombL2,combtuningL2);
combR[1].setbuffer(bufcombR2,combtuningR2);
combL[2].setbuffer(bufcombL3,combtuningL3);
combR[2].setbuffer(bufcombR3,combtuningR3);
combL[3].setbuffer(bufcombL4,combtuningL4);
combR[3].setbuffer(bufcombR4,combtuningR4);
combL[4].setbuffer(bufcombL5,combtuningL5);
combR[4].setbuffer(bufcombR5,combtuningR5);
combL[5].setbuffer(bufcombL6,combtuningL6);
combR[5].setbuffer(bufcombR6,combtuningR6);
combL[6].setbuffer(bufcombL7,combtuningL7);
combR[6].setbuffer(bufcombR7,combtuningR7);
combL[7].setbuffer(bufcombL8,combtuningL8);
combR[7].setbuffer(bufcombR8,combtuningR8);
allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
// Set default values
allpassL[0].setfeedback(0.5f);
allpassR[0].setfeedback(0.5f);
allpassL[1].setfeedback(0.5f);
allpassR[1].setfeedback(0.5f);
allpassL[2].setfeedback(0.5f);
allpassR[2].setfeedback(0.5f);
allpassL[3].setfeedback(0.5f);
allpassR[3].setfeedback(0.5f);
setwet(initialwet);
setroomsize(initialroom);
setdry(initialdry);
setdamp(initialdamp);
setwidth(initialwidth);
setmode(initialmode);
// Buffer will be full of rubbish - so we MUST mute them
mute();
}
void revmodel::mute()
{
int i;
if (getmode() >= freezemode)
return;
for (i = 0 ; i < numcombs ; i++)
{
combL[i].mute();
combR[i].mute();
}
for (i=0;i<numallpasses;i++)
{
allpassL[i].mute();
allpassR[i].mute();
}
}
void revmodel::processreplace(float *inputL, float *outputL, long numsamples, int skip)
{
float outL,outR,input;
float inputR;
int i;
outL = outR = 0;
if (skip > 1)
inputR = inputL[1];
else
inputR = inputL[0];
input = (inputL[0] + inputR) * gain;
// Accumulate comb filters in parallel
for(i=0; i<numcombs; i++)
{
outL += combL[i].process(input);
outR += combR[i].process(input);
}
// Feed through allpasses in series
for(i=0; i<numallpasses; i++)
{
outL = allpassL[i].process(outL);
outR = allpassR[i].process(outR);
}
// Calculate output REPLACING anything already there
outputL[0] = (outL*wet1 + outR*wet2 + inputR*dry);
if (skip > 1)
outputL[1] = (outR*wet1 + outL*wet2 + inputR*dry);
}
void revmodel::processmix(float *inputL, float *outputL, long numsamples, int skip)
{
float outL,outR,input;
float inputR;
int i;
outL = outR = 0;
if (skip > 1)
inputR = inputL[1];
else
inputR = inputL[0];
input = (inputL[0] + inputR) * gain;
// Accumulate comb filters in parallel
for(i=0; i<numcombs; i++)
{
outL += combL[i].process(input);
outR += combR[i].process(input);
}
// Feed through allpasses in series
for(i=0; i<numallpasses; i++)
{
outL = allpassL[i].process(outL);
outR = allpassR[i].process(outR);
}
// Calculate output REPLACING anything already there
outputL[0] += (outL*wet1 + outR*wet2 + inputR*dry);
if (skip > 1)
outputL[1] += (outR*wet1 + outL*wet2 + inputR*dry);
}
void revmodel::update()
{
// Recalculate internal values after parameter change
int i;
wet1 = wet*(width/2 + 0.5f);
wet2 = wet*((1-width)/2);
if (mode >= freezemode)
{
roomsize1 = 1;
damp1 = 0;
gain = muted;
}
else
{
roomsize1 = roomsize;
damp1 = damp;
gain = fixedgain;
}
for(i=0; i<numcombs; i++)
{
combL[i].setfeedback(roomsize1);
combR[i].setfeedback(roomsize1);
}
for(i=0; i<numcombs; i++)
{
combL[i].setdamp(damp1);
combR[i].setdamp(damp1);
}
}
// The following get/set functions are not inlined, because
// speed is never an issue when calling them, and also
// because as you develop the reverb model, you may
// wish to take dynamic action when they are called.
void revmodel::setroomsize(float value)
{
roomsize = (value*scaleroom) + offsetroom;
update();
}
float revmodel::getroomsize()
{
return (roomsize-offsetroom)/scaleroom;
}
void revmodel::setdamp(float value)
{
damp = value*scaledamp;
update();
}
float revmodel::getdamp()
{
return damp/scaledamp;
}
void revmodel::setwet(float value)
{
wet = value*scalewet;
update();
}
float revmodel::getwet()
{
return wet/scalewet;
}
void revmodel::setdry(float value)
{
dry = value*scaledry;
}
float revmodel::getdry()
{
return dry/scaledry;
}
void revmodel::setwidth(float value)
{
width = value;
update();
}
float revmodel::getwidth()
{
return width;
}
void revmodel::setmode(float value)
{
mode = value;
update();
}
float revmodel::getmode()
{
if (mode >= freezemode)
return 1;
else
return 0;
}
//ends
// Reverb model declaration
//
// Google Summer of Code 2007
//
// Authors: Biodun Osunkunle <biodun@videolan.org>
//
// Mentor : Jean-Baptiste Kempf <jb@videolan.org>
//
// Original written by Jezar at Dreampoint, June 2000
#ifndef _revmodel_
#define _revmodel_
#include "comb.hpp"
#include "allpass.hpp"
#include "tuning.h"
class revmodel
{
public:
revmodel();
void mute();
void processreplace(float *inputL, float *outputL, long numsamples, int skip);
void processmix(float *inputL, float *outputL, long numsamples, int skip);
void setroomsize(float value);
float getroomsize();
void setdamp(float value);
float getdamp();
void setwet(float value);
float getwet();
void setdry(float value);
float getdry();
void setwidth(float value);
float getwidth();
void setmode(float value);
float getmode();
private:
void update();
private:
float gain;
float roomsize,roomsize1;
float damp,damp1;
float wet,wet1,wet2;
float dry;
float width;
float mode;
// The following are all declared inline
// to remove the need for dynamic allocation
// with its subsequent error-checking messiness
// Comb filters
comb combL[numcombs];
comb combR[numcombs];
// Allpass filters
allpass allpassL[numallpasses];
allpass allpassR[numallpasses];
// Buffers for the combs
float bufcombL1[combtuningL1];
float bufcombR1[combtuningR1];
float bufcombL2[combtuningL2];
float bufcombR2[combtuningR2];
float bufcombL3[combtuningL3];
float bufcombR3[combtuningR3];
float bufcombL4[combtuningL4];
float bufcombR4[combtuningR4];
float bufcombL5[combtuningL5];
float bufcombR5[combtuningR5];
float bufcombL6[combtuningL6];
float bufcombR6[combtuningR6];
float bufcombL7[combtuningL7];
float bufcombR7[combtuningR7];
float bufcombL8[combtuningL8];
float bufcombR8[combtuningR8];
// Buffers for the allpasses
float bufallpassL1[allpasstuningL1];
float bufallpassR1[allpasstuningR1];
float bufallpassL2[allpasstuningL2];
float bufallpassR2[allpasstuningR2];
float bufallpassL3[allpasstuningL3];
float bufallpassR3[allpasstuningR3];
float bufallpassL4[allpasstuningL4];
float bufallpassR4[allpasstuningR4];
};
#endif//_revmodel_
//ends
/*****************************************************************************
* spatializer.cpp:
*****************************************************************************
* Copyright (C) 2004, 2006, 2007 the VideoLAN team
*
* Google Summer of Code 2007
*
* Authors: Biodun Osunkunle <biodun@videolan.org>
*
* Mentor : Jean-Baptiste Kempf <jb@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <math.h>
#include <vlc/vlc.h>
#include "vlc_aout.h"
#include "revmodel.hpp"
#define SPAT_AMP 0.3
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
vlc_module_begin();
set_description( _("spatializer") );
set_shortname( _("spatializer" ) );
set_capability( "audio filter", 0 );
set_category( CAT_AUDIO );
set_subcategory( SUBCAT_AUDIO_AFILTER );
set_callbacks( Open, Close );
add_shortcut( "spatializer" );
add_float( "Roomsize", 1.05, NULL, NULL,NULL, VLC_TRUE);
add_float( "Width", 10.0, NULL, NULL,NULL, VLC_TRUE);
add_float( "Wet", 3.0, NULL, NULL,NULL, VLC_TRUE);
add_float( "Dry", 2.0, NULL, NULL,NULL, VLC_TRUE);
add_float( "Damp", 1.0, NULL, NULL,NULL, VLC_TRUE);
vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct aout_filter_sys_t
{
/* reverb static config */
vlc_bool_t b_first;
} aout_filter_sys_t;
static revmodel reverbm;
static const char *psz_control_names[] =
{
"Roomsize", "Width" , "Wet", "Dry", "Damp"
};
static void DoWork( aout_instance_t *, aout_filter_t *,
aout_buffer_t *, aout_buffer_t * );
static int SpatInit( aout_filter_t *);
static void SpatFilter( aout_instance_t *,aout_filter_t *, float *, float *,
int, int );
static void SpatClean( aout_filter_t * );
static int RoomCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int WetCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int DryCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int DampCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int WidthCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
aout_filter_t *p_filter = (aout_filter_t *)p_this;
aout_filter_sys_t *p_sys;
vlc_bool_t b_fit = VLC_TRUE;
msg_Dbg(p_this, "Opening filter spatializer %s %s %d\n", __FILE__,__func__,__LINE__);
if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
{
b_fit = VLC_FALSE;
p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
msg_Warn( p_filter, "bad input or output format" );
}
if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
{
b_fit = VLC_FALSE;
memcpy( &p_filter->output, &p_filter->input,
sizeof(audio_sample_format_t) );
msg_Warn( p_filter, "input and output formats are not similar" );
}
if ( ! b_fit )
{
return VLC_EGENERIC;
}
p_filter->pf_do_work = DoWork;
p_filter->b_in_place = VLC_TRUE;
/* Allocate structure */
p_sys = p_filter->p_sys = (aout_filter_sys_t*)malloc( sizeof( aout_filter_sys_t ) );
reverbm.setroomsize(1.05);
reverbm.setwet(10.0f);
reverbm.setdry(1.0f);
reverbm.setdamp(0.3);
reverbm.setwidth(0.9);
SpatInit( p_filter);
return VLC_SUCCESS;
}
/*****************************************************************************
* Close: close the plugin
*****************************************************************************/
static void Close( vlc_object_t *p_this )
{
aout_filter_t *p_filter = (aout_filter_t *)p_this;
aout_filter_sys_t *p_sys = p_filter->p_sys;
SpatClean( p_filter );
free( p_sys );
msg_Dbg(p_this, "Closing filter spatializer %s %s %d\n", __FILE__,__func__,__LINE__);
}
/*****************************************************************************
* DoWork: process samples buffer
*****************************************************************************
*
*****************************************************************************/
static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
{
p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes;
SpatFilter( p_aout, p_filter, (float*)p_out_buf->p_buffer,
(float*)p_in_buf->p_buffer, p_in_buf->i_nb_samples,
aout_FormatNbChannels( &p_filter->input ) );
}
static int SpatInit( aout_filter_t *p_filter )
{
aout_filter_sys_t *p_sys = p_filter->p_sys;
int i, ch;
vlc_value_t val1, val2, val3, val4, val5;
aout_instance_t *p_aout = (aout_instance_t *)p_filter->p_parent;
for( int i = 0; i < 5 ; i ++ )
{
var_CreateGetFloatCommand( p_aout, psz_control_names[i] );
}
/* Get initial values */
var_Get( p_aout, psz_control_names[0], &val1 );
var_Get( p_aout, psz_control_names[1], &val2 );
var_Get( p_aout, psz_control_names[2], &val3 );
var_Get( p_aout, psz_control_names[3], &val4 );
var_Get( p_aout, psz_control_names[4], &val5);
RoomCallback( VLC_OBJECT( p_aout ), NULL, val1, val1, p_sys );
WidthCallback( VLC_OBJECT( p_aout ), NULL, val2, val2, p_sys );
WetCallback( VLC_OBJECT( p_aout ), NULL, val3, val3, p_sys );
DryCallback( VLC_OBJECT( p_aout ), NULL, val4, val4, p_sys );
DampCallback( VLC_OBJECT( p_aout ), NULL, val5, val5, p_sys );
msg_Dbg( p_filter, "%f", val1.f_float );
/* Add our own callbacks */
var_AddCallback( p_aout, psz_control_names[0], RoomCallback, p_sys );
var_AddCallback( p_aout, psz_control_names[1], WidthCallback, p_sys );
var_AddCallback( p_aout, psz_control_names[2], WetCallback, p_sys );
var_AddCallback( p_aout, psz_control_names[3], DryCallback, p_sys );
var_AddCallback( p_aout, psz_control_names[4], DampCallback, p_sys );
return VLC_SUCCESS;
}
static void SpatFilter( aout_instance_t *p_aout,
aout_filter_t *p_filter, float *out, float *in,
int i_samples, int i_channels )
{
int i, ch, j;
for( i = 0; i < i_samples; i++ )
{
for( ch = 0 ; ch < 2; ch++)
{
in[ch] = in[ch] * SPAT_AMP;
}
reverbm.processreplace( in, out , 1, i_channels);
in += i_channels;
out += i_channels;
}
}
static void SpatClean( aout_filter_t *p_filter )
{
aout_filter_sys_t *p_sys = p_filter->p_sys;
var_DelCallback( (aout_instance_t *)p_filter->p_parent,
"Roomsize", RoomCallback, p_sys );
var_DelCallback( (aout_instance_t *)p_filter->p_parent,
"Width", WidthCallback, p_sys );
var_DelCallback( (aout_instance_t *)p_filter->p_parent,
"Wet", WetCallback, p_sys );
var_DelCallback( (aout_instance_t *)p_filter->p_parent,
"Dry", DryCallback, p_sys );
var_DelCallback( (aout_instance_t *)p_filter->p_parent,
"Damp", DampCallback, p_sys );
}
static int RoomCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
msg_Dbg (p_this,"room callback %3.1f %s %s %d\n", newval.f_float, __FILE__,__func__,__LINE__);
reverbm.setroomsize(newval.f_float);
return VLC_SUCCESS;
}
static int WidthCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
reverbm.setwidth(newval.f_float);
msg_Dbg (p_this,"width callback %3.1f %s %s %d\n", newval.f_float, __FILE__,__func__,__LINE__);
return VLC_SUCCESS;
}
static int WetCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
reverbm.setwet(newval.f_float);
msg_Dbg (p_this,"wet callback %3.1f %s %s %d\n", newval.f_float, __FILE__,__func__,__LINE__);
return VLC_SUCCESS;
}
static int DryCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
reverbm.setdry(newval.f_float);
msg_Dbg (p_this,"dry callback %3.1f %s %s %d\n", newval.f_float, __FILE__,__func__,__LINE__);
return VLC_SUCCESS;
}
static int DampCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
reverbm.setdamp(newval.f_float);
msg_Dbg (p_this, "damp callback %3.1f %s %s %d\n", newval.f_float, __FILE__,__func__,__LINE__);
return VLC_SUCCESS;
}
// Reverb model tuning values
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#ifndef _tuning_
#define _tuning_
const int numcombs = 8;
const int numallpasses = 4;
const float muted = 0;
const float fixedgain = 0.005f;
const float scalewet = 3;
const float scaledry = 2;
const float scaledamp = 0.4f;
const float scaleroom = 0.28f;
const float offsetroom = 0.7f;
const float initialroom = 0.5f;
const float initialdamp = 0.5f;
const float initialwet = 1/scalewet;
const float initialdry = 0;
const float initialwidth = 1;
const float initialmode = 0;
const float freezemode = 0.5f;
const int stereospread = 23;
// These values assume 44.1KHz sample rate
// they will probably be OK for 48KHz sample rate
// but would need scaling for 96KHz (or other) sample rates.
// The values were obtained by listening tests.
const int combtuningL1 = 1116;
const int combtuningR1 = 1116+stereospread;
const int combtuningL2 = 1188;
const int combtuningR2 = 1188+stereospread;
const int combtuningL3 = 1277;
const int combtuningR3 = 1277+stereospread;
const int combtuningL4 = 1356;
const int combtuningR4 = 1356+stereospread;
const int combtuningL5 = 1422;
const int combtuningR5 = 1422+stereospread;
const int combtuningL6 = 1491;
const int combtuningR6 = 1491+stereospread;
const int combtuningL7 = 1557;
const int combtuningR7 = 1557+stereospread;
const int combtuningL8 = 1617;
const int combtuningR8 = 1617+stereospread;
const int allpasstuningL1 = 556;
const int allpasstuningR1 = 556+stereospread;
const int allpasstuningL2 = 441;
const int allpasstuningR2 = 441+stereospread;
const int allpasstuningL3 = 341;
const int allpasstuningR3 = 341+stereospread;
const int allpasstuningL4 = 225;
const int allpasstuningR4 = 225+stereospread;
#endif//_tuning_
//ends
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment