@ -1,18 +1,24 @@
/*
* Copyright 2014 Pascal Gauthier .
* Copyright 2012 Google Inc .
* Copyright ( C ) 2015 Pascal Gauthier
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
* 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 3 of the License , or
* ( at your option ) any later version .
*
* http : //www.apache.org/licenses/LICENSE-2.0
* 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
*
* The code is based on ppplay https : //github.com/stohrendorf/ppplay and opl3
* math documentation :
* https : //github.com/gtaylormb/opl3_fpga/blob/master/docs/opl3math/opl3math.pdf
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
# include "EngineMkI.h"
@ -23,10 +29,10 @@
# include "msfa/sin.h"
# include "msfa/exp2.h"
static const uint16_t MKI_BITDEPTH = 8 ;
static const uint16_t MKI_TABLESIZE = 1 < < MKI_BITDEPTH ;
static const uint16_t MKI_TABLEFILTER = MKI_TABLESIZE - 1 ;
static const uint16_t NEGATIVE_BIT = 0x8000 ;
# ifdef DEBUG
# include "time.h"
//#define MKIDEBUG
# endif
# ifdef _WIN32
__declspec ( align ( 16 ) ) int zeros [ N ] = { 0 } ;
@ -34,63 +40,103 @@ static const uint16_t NEGATIVE_BIT = 0x8000;
const int32_t __attribute__ ( ( aligned ( 16 ) ) ) zeros [ N ] = { 0 } ;
# endif
static uint16_t sinLogTable [ MKI_TABLESIZE ] ;
static uint16_t sinExpTable [ MKI_TABLESIZE ] ;
static const uint16_t NEGATIVE_BIT = 0x8000 ;
static const uint16_t ENV_BITDEPTH = 14 ;
static const uint16_t SINLOG_BITDEPTH = 10 ;
static const uint16_t SINLOG_TABLESIZE = 1 < < SINLOG_BITDEPTH ;
static uint16_t sinLogTable [ SINLOG_TABLESIZE ] ;
static const uint16_t SINEXP_BITDEPTH = 10 ;
static const uint16_t SINEXP_TABLESIZE = 1 < < SINEXP_BITDEPTH ;
static uint16_t sinExpTable [ SINEXP_TABLESIZE ] ;
static inline uint16_t sinLog ( uint16_t phi ) {
const uint16_t SINLOG_TABLEFILTER = SINLOG_TABLESIZE - 1 ;
const uint16_t index = ( phi & SINLOG_TABLEFILTER ) ;
switch ( ( phi & ( SINLOG_TABLESIZE * 3 ) ) ) {
case 0 :
return sinLogTable [ index ] ;
case SINLOG_TABLESIZE :
return sinLogTable [ index ^ SINLOG_TABLEFILTER ] ;
case SINLOG_TABLESIZE * 2 :
return sinLogTable [ index ] | NEGATIVE_BIT ;
default :
return sinLogTable [ index ^ SINLOG_TABLEFILTER ] | NEGATIVE_BIT ;
}
}
EngineMkI : : EngineMkI ( ) {
float bitReso = MKI_TABLESIZE ;
float bitReso = SINLOG _TABLESIZE;
for ( int i = 0 ; i < MKI_TABLESIZE ; i + + ) {
for ( int i = 0 ; i < SINLOG _TABLESIZE; i + + ) {
float x1 = sin ( ( ( 0.5 + i ) / bitReso ) * M_PI / 2.0 ) ;
sinLogTable [ i ] = round ( - bitReso * log2 ( x1 ) ) ;
sinLogTable [ i ] = round ( - 1024 * log2 ( x1 ) ) ;
}
bitReso = MKI_TABLESIZE ;
for ( int i = 0 ; i < MKI_TABLESIZE ; i + + ) {
float x1 = ( pow ( 2 , float ( i ) / bitReso ) - 1 ) * 1024 ;
bitReso = SINEXP _TABLESIZE;
for ( int i = 0 ; i < SINEXP _TABLESIZE; i + + ) {
float x1 = ( pow ( 2 , float ( i ) / bitReso ) - 1 ) * 4096 ;
sinExpTable [ i ] = round ( x1 ) ;
}
}
static inline uint16_t sinLog ( uint16_t phi ) {
const uint16_t index = ( phi & MKI_TABLEFILTER ) ;
switch ( ( phi & ( MKI_TABLESIZE * 3 ) ) ) {
case 0x0000 :
// rising quarter wave Shape A
return sinLogTable [ index ] ;
case MKI_TABLESIZE :
// falling quarter wave Shape B
return sinLogTable [ index ^ MKI_TABLEFILTER ] ;
case ( MKI_TABLESIZE * 2 ) :
// rising quarter wave -ve Shape C
return sinLogTable [ index ] | NEGATIVE_BIT ;
default :
// falling quarter wave -ve Shape D
return sinLogTable [ index ^ MKI_TABLEFILTER ] | NEGATIVE_BIT ;
# ifdef MKIDEBUG
char buffer [ 4096 ] ;
int pos = 0 ;
TRACE ( " **************************************** " ) ;
for ( int i = 0 ; i < SINLOG_TABLESIZE ; i + + ) {
pos + = sprintf ( buffer + pos , " %d " , sinLogTable [ i ] ) ;
if ( pos > 90 ) {
TRACE ( " SINLOGTABLE: %s " , buffer ) ;
buffer [ 0 ] = 0 ;
pos = 0 ;
}
}
TRACE ( " SINLOGTABLE: %s " , buffer ) ;
buffer [ 0 ] = 0 ;
pos = 0 ;
TRACE ( " ---------------------------------------- " ) ;
for ( int i = 0 ; i < SINEXP_TABLESIZE ; i + + ) {
pos + = sprintf ( buffer + pos , " %d " , sinExpTable [ i ] ) ;
if ( pos > 90 ) {
TRACE ( " SINEXTTABLE: %s " , buffer ) ;
buffer [ 0 ] = 0 ;
pos = 0 ;
}
}
TRACE ( " SINEXTTABLE: %s " , buffer ) ;
TRACE ( " **************************************** " ) ;
# endif
}
inline int32_t mkiSin ( int32_t phase , uint16_t env ) {
const uint16_t shift = 22 - MKI_BITDEPTH ;
uint16_t expVal = sinLog ( phase > > shift ) + ( env < < 3 ) ;
const bool isSigned = expVal & NEGATIVE_BIT ;
uint16_t expVal = sinLog ( phase > > ( 22 - SINLOG_BITDEPTH ) ) + ( env ) ;
//int16_t expValShow = expVal;
const bool isSigned = expVal & NEGATIVE_BIT ;
expVal & = ~ NEGATIVE_BIT ;
// expVal: 0..2137+511*8 = 0..6225
// result: 0..1018+1024
uint16_t result = 0x0400 + sinExpTable [ ( expVal & MKI_TABLEFILTER ) ^ MKI_TABLEFILTER ] ;
result < < = 1 ;
result > > = ( expVal > > 8 ) ; // exp
uint32_t ret ;
if ( isSigned ) {
// -1 for one's complement
ret = - result - 1 ;
} else {
ret = result ;
const uint16_t SINEXP_FILTER = 0x3FF ;
uint16_t result = 4096 + sinExpTable [ ( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER ] ;
//uint16_t resultB4 = result;
result > > = ( expVal > > 10 ) ; // exp
# ifdef MKIDEBUG
if ( ( time ( NULL ) % 5 ) = = 0 ) {
if ( expValShow < 0 ) {
expValShow = ( expValShow + 0x7FFF ) * - 1 ;
}
//TRACE(",%d,%d,%d,%d,%d,%d", phase >> (22 - SINLOG_BITDEPTH), env, expValShow, ( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER, resultB4, result);
}
return ret < < shift ;
# endif
if ( isSigned )
return ( - result - 1 ) < < 13 ;
else
return result < < 13 ;
}
void EngineMkI : : compute ( int32_t * output , const int32_t * input ,
@ -148,44 +194,50 @@ void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
fb_buf [ 1 ] = y ;
}
void EngineMkI : : render ( int32_t * output , FmOpParams * params , int algorithm ,
int32_t * fb_buf , int feedback_shift ) {
const uint16_t ENV_MAX = 1 < < ENV_BITDEPTH ;
const uint16_t kLevelThresh = ENV_MAX - 100 ; // really ???? uhuhuh
const FmAlgorithm alg = algorithms [ algorithm ] ;
bool has_contents [ 3 ] = { true , false , false } ;
for ( int op = 0 ; op < 6 ; op + + ) {
int flags = alg . ops [ op ] ;
bool add = ( flags & OUT_BUS_ADD ) ! = 0 ;
FmOpParams & param = params [ op ] ;
int inbus = ( flags > > 4 ) & 3 ;
int outbus = flags & 3 ;
int32_t * outptr = ( outbus = = 0 ) ? output : buf_ [ outbus - 1 ] . get ( ) ;
int32_t gain1 = param . gain_out = = 0 ? ( ENV_MAX - 1 ) : param . gain_out ;
int32_t gain2 = ENV_MAX - ( param . level_in > > ( 28 - ENV_BITDEPTH ) ) ;
param . gain_out = gain2 ;
// exclusively used for ALGO 6 with feedback
void EngineMkI : : compute_fb2 ( int32_t * output , FmOpParams * parms , int32_t gain01 , int32_t gain02 , int32_t * fb_buf , int fb_shift ) {
int32_t dgain [ 2 ] ;
int32_t gain [ 2 ] ;
int32_t phase [ 2 ] ;
int32_t y0 = fb_buf [ 0 ] ;
int32_t y = fb_buf [ 1 ] ;
phase [ 0 ] = parms [ 0 ] . phase ;
phase [ 1 ] = parms [ 1 ] . phase ;
gain [ 0 ] = gain01 ;
gain [ 1 ] = parms [ 1 ] . gain_out ;
dgain [ 0 ] = ( gain02 - gain01 + ( N > > 1 ) ) > > LG_N ;
parms [ 1 ] . gain_out = Exp2 : : lookup ( parms [ 1 ] . level_in - ( 14 * ( 1 < < 24 ) ) ) ;
dgain [ 1 ] = ( parms [ 1 ] . gain_out - gain [ 1 ] + ( N > > 1 ) ) > > LG_N ;
for ( int i = 0 ; i < N ; i + + ) {
// op 0
gain [ 0 ] + = dgain [ 0 ] ;
int32_t scaled_fb = ( y0 + y ) > > ( fb_shift + 2 ) ; // tsk tsk tsk: this needs some tuning
y0 = y ;
y = Sin : : lookup ( phase [ 0 ] + scaled_fb ) ;
y = ( ( int64_t ) y * ( int64_t ) gain [ 0 ] ) > > 24 ;
phase [ 0 ] + = parms [ 0 ] . freq ;
// op 1
gain [ 1 ] + = dgain [ 1 ] ;
y = Sin : : lookup ( phase [ 1 ] + y ) ;
y = ( ( int64_t ) y * ( int64_t ) gain [ 1 ] ) > > 24 ;
output [ i ] = y ;
phase [ 1 ] + = parms [ 1 ] . freq ;
if ( gain1 < = kLevelThresh | | gain2 < = kLevelThresh ) {
if ( ! has_contents [ outbus ] ) {
add = false ;
}
if ( inbus = = 0 | | ! has_contents [ inbus ] ) {
// todo: more than one op in a feedback loop
if ( ( flags & 0xc0 ) = = 0xc0 & & feedback_shift < 16 ) {
// cout << op << " fb " << inbus << outbus << add << endl;
compute_fb ( outptr , param . phase , param . freq ,
gain1 , gain2 ,
fb_buf , feedback_shift , add ) ;
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
compute_pure ( outptr , param . phase , param . freq ,
gain1 , gain2 , add ) ;
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
compute ( outptr , buf_ [ inbus - 1 ] . get ( ) ,
param . phase , param . freq , gain1 , gain2 , add ) ;
}
has_contents [ outbus ] = true ;
} else if ( ! add ) {
has_contents [ outbus ] = false ;
}
param . phase + = param . freq < < LG_N ;
}
fb_buf [ 0 ] = y0 ;
fb_buf [ 1 ] = y ;
}
const FmAlgorithm EngineMkI : : algo2 [ 32 ] = {
@ -272,49 +324,43 @@ void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01,
fb_buf [ 1 ] = y ;
}
void EngineMkI : : render ( int32_t * output , FmOpParams * params , int algorithm ,
int32_t * fb_buf , int feedback_shift ) {
const int kLevelThresh = 507 ; // really ????
const FmAlgorithm alg = algorithms [ algorithm ] ;
bool has_contents [ 3 ] = { true , false , false } ;
for ( int op = 0 ; op < 6 ; op + + ) {
int flags = alg . ops [ op ] ;
bool add = ( flags & OUT_BUS_ADD ) ! = 0 ;
FmOpParams & param = params [ op ] ;
int inbus = ( flags > > 4 ) & 3 ;
int outbus = flags & 3 ;
int32_t * outptr = ( outbus = = 0 ) ? output : buf_ [ outbus - 1 ] . get ( ) ;
int32_t gain1 = param . gain_out = = 0 ? 511 : param . gain_out ;
int32_t gain2 = 512 - ( param . level_in > > 19 ) ;
param . gain_out = gain2 ;
// exclusively used for ALGO 6 with feedback
void EngineMkI : : compute_fb2 ( int32_t * output , FmOpParams * parms , int32_t gain01 , int32_t gain02 , int32_t * fb_buf , int fb_shift ) {
int32_t dgain [ 2 ] ;
int32_t gain [ 2 ] ;
int32_t phase [ 2 ] ;
int32_t y0 = fb_buf [ 0 ] ;
int32_t y = fb_buf [ 1 ] ;
if ( gain1 < = kLevelThresh | | gain2 < = kLevelThresh ) {
if ( ! has_contents [ outbus ] ) {
add = false ;
}
if ( inbus = = 0 | | ! has_contents [ inbus ] ) {
// todo: more than one op in a feedback loop
if ( ( flags & 0xc0 ) = = 0xc0 & & feedback_shift < 16 ) {
// cout << op << " fb " << inbus << outbus << add << endl;
compute_fb ( outptr , param . phase , param . freq ,
gain1 , gain2 ,
fb_buf , feedback_shift , add ) ;
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
compute_pure ( outptr , param . phase , param . freq ,
gain1 , gain2 , add ) ;
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
compute ( outptr , buf_ [ inbus - 1 ] . get ( ) ,
param . phase , param . freq , gain1 , gain2 , add ) ;
}
has_contents [ outbus ] = true ;
} else if ( ! add ) {
has_contents [ outbus ] = false ;
}
param . phase + = param . freq < < LG_N ;
phase [ 0 ] = parms [ 0 ] . phase ;
phase [ 1 ] = parms [ 1 ] . phase ;
gain [ 0 ] = gain01 ;
gain [ 1 ] = parms [ 1 ] . gain_out ;
dgain [ 0 ] = ( gain02 - gain01 + ( N > > 1 ) ) > > LG_N ;
parms [ 1 ] . gain_out = Exp2 : : lookup ( parms [ 1 ] . level_in - ( 14 * ( 1 < < 24 ) ) ) ;
dgain [ 1 ] = ( parms [ 1 ] . gain_out - gain [ 1 ] + ( N > > 1 ) ) > > LG_N ;
for ( int i = 0 ; i < N ; i + + ) {
// op 0
gain [ 0 ] + = dgain [ 0 ] ;
int32_t scaled_fb = ( y0 + y ) > > ( fb_shift + 2 ) ; // tsk tsk tsk: this needs some tuning
y0 = y ;
y = Sin : : lookup ( phase [ 0 ] + scaled_fb ) ;
y = ( ( int64_t ) y * ( int64_t ) gain [ 0 ] ) > > 24 ;
phase [ 0 ] + = parms [ 0 ] . freq ;
// op 1
gain [ 1 ] + = dgain [ 1 ] ;
y = Sin : : lookup ( phase [ 1 ] + y ) ;
y = ( ( int64_t ) y * ( int64_t ) gain [ 1 ] ) > > 24 ;
output [ i ] = y ;
phase [ 1 ] + = parms [ 1 ] . freq ;
}
fb_buf [ 0 ] = y0 ;
fb_buf [ 1 ] = y ;
}
/*