mirror of https://github.com/dcoredump/dexed.git
parent
77fa26a066
commit
05cd3aeba9
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,257 +1,272 @@ |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifdef VERBOSE |
||||
#include <iostream> |
||||
#endif |
||||
#include <math.h> |
||||
#include "synth.h" |
||||
#include "freqlut.h" |
||||
#include "exp2.h" |
||||
#include "controllers.h" |
||||
#include "dx7note.h" |
||||
|
||||
using namespace std; |
||||
|
||||
void dexed_trace(const char *source, const char *fmt, ...); |
||||
|
||||
#ifdef _MSC_VER |
||||
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__) |
||||
#else |
||||
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__) |
||||
#endif |
||||
|
||||
int32_t midinote_to_logfreq(int midinote) { |
||||
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
|
||||
const int step = (1 << 24) / 12; |
||||
return base + step * midinote; |
||||
} |
||||
|
||||
const int32_t coarsemul[] = { |
||||
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600, |
||||
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816, |
||||
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858, |
||||
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032, |
||||
81503396, 82323963, 83117622 |
||||
}; |
||||
|
||||
int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) { |
||||
// TODO: pitch randomization
|
||||
int32_t logfreq; |
||||
if (mode == 0) { |
||||
logfreq = midinote_to_logfreq(midinote); |
||||
logfreq += coarsemul[coarse & 31]; |
||||
if (fine) { |
||||
// (1 << 24) / log(2)
|
||||
logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5); |
||||
} |
||||
// This was measured at 7.213Hz per count at 9600Hz, but the exact
|
||||
// value is somewhat dependent on midinote. Close enough for now.
|
||||
logfreq += 12606 * (detune - 7); |
||||
} else { |
||||
// ((1 << 24) * log(10) / log(2) * .01) << 3
|
||||
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3; |
||||
logfreq += detune > 7 ? 13457 * (detune - 7) : 0; |
||||
} |
||||
return logfreq; |
||||
} |
||||
|
||||
const uint8_t velocity_data[64] = { |
||||
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163, |
||||
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202, |
||||
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232, |
||||
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251, |
||||
252, 253, 254 |
||||
}; |
||||
|
||||
// See "velocity" section of notes. Returns velocity delta in microsteps.
|
||||
int ScaleVelocity(int velocity, int sensitivity) { |
||||
int clamped_vel = max(0, min(127, velocity)); |
||||
int vel_value = velocity_data[clamped_vel >> 1] - 239; |
||||
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4; |
||||
return scaled_vel; |
||||
} |
||||
|
||||
int ScaleRate(int midinote, int sensitivity) { |
||||
int x = min(31, max(0, midinote / 3 - 7)); |
||||
int qratedelta = (sensitivity * x) >> 3; |
||||
#ifdef SUPER_PRECISE |
||||
int rem = x & 7; |
||||
if (sensitivity == 3 && rem == 3) { |
||||
qratedelta -= 1; |
||||
} else if (sensitivity == 7 && rem > 0 && rem < 4) { |
||||
qratedelta += 1; |
||||
} |
||||
#endif |
||||
return qratedelta; |
||||
} |
||||
|
||||
const uint8_t exp_scale_data[] = { |
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66, |
||||
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250 |
||||
}; |
||||
|
||||
int ScaleCurve(int group, int depth, int curve) { |
||||
int scale; |
||||
if (curve == 0 || curve == 3) { |
||||
// linear
|
||||
scale = (group * depth * 329) >> 12; |
||||
} else { |
||||
// exponential
|
||||
int n_scale_data = sizeof(exp_scale_data); |
||||
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)]; |
||||
scale = (raw_exp * depth * 329) >> 15; |
||||
} |
||||
if (curve < 2) { |
||||
scale = -scale; |
||||
} |
||||
return scale; |
||||
} |
||||
|
||||
int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth, |
||||
int left_curve, int right_curve) { |
||||
int offset = midinote - break_pt - 17; |
||||
if (offset >= 0) { |
||||
return ScaleCurve(offset / 3, right_depth, right_curve); |
||||
} else { |
||||
return ScaleCurve((-offset) / 3, left_depth, left_curve); |
||||
} |
||||
} |
||||
|
||||
static const uint8_t pitchmodsenstab[] = { |
||||
0, 10, 20, 33, 55, 92, 153, 255 |
||||
}; |
||||
|
||||
void Dx7Note::init(const char patch[156], int midinote, int velocity) { |
||||
int rates[4]; |
||||
int levels[4]; |
||||
for (int op = 0; op < 6; op++) { |
||||
int off = op * 21; |
||||
for (int i = 0; i < 4; i++) { |
||||
rates[i] = patch[off + i]; |
||||
levels[i] = patch[off + 4 + i]; |
||||
} |
||||
int outlevel = patch[off + 16]; |
||||
outlevel = Env::scaleoutlevel(outlevel); |
||||
#ifdef VERBOSE |
||||
for (int j = 8; j < 12; j++) { |
||||
cout << (int)patch[off + j] << " "; |
||||
} |
||||
#endif |
||||
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9], |
||||
patch[off + 10], patch[off + 11], patch[off + 12]); |
||||
outlevel += level_scaling; |
||||
outlevel = min(127, outlevel); |
||||
#ifdef VERBOSE |
||||
cout << op << ": " << level_scaling << " " << outlevel << endl; |
||||
#endif |
||||
outlevel = outlevel << 5; |
||||
outlevel += ScaleVelocity(velocity, patch[off + 15]); |
||||
outlevel = max(0, outlevel); |
||||
int rate_scaling = ScaleRate(midinote, patch[off + 13]); |
||||
env_[op].init(rates, levels, outlevel, rate_scaling); |
||||
|
||||
int mode = patch[off + 17]; |
||||
int coarse = patch[off + 18]; |
||||
int fine = patch[off + 19]; |
||||
int detune = patch[off + 20]; |
||||
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune); |
||||
basepitch_[op] = freq; |
||||
// cout << op << " freq: " << freq << endl;
|
||||
params_[op].phase = 0; |
||||
params_[op].gain[1] = 0; |
||||
} |
||||
for (int i = 0; i < 4; i++) { |
||||
rates[i] = patch[126 + i]; |
||||
levels[i] = patch[130 + i]; |
||||
} |
||||
pitchenv_.set(rates, levels); |
||||
algorithm_ = patch[134]; |
||||
int feedback = patch[135]; |
||||
fb_shift_ = feedback != 0 ? 8 - feedback : 16; |
||||
pitchmoddepth_ = (patch[139] * 165) >> 6; |
||||
pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; |
||||
ampmoddepth_ = (patch[140] * 165) >> 6; |
||||
} |
||||
|
||||
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, |
||||
const Controllers *ctrls) { |
||||
int32_t pitchmod = pitchenv_.getsample(); |
||||
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
|
||||
// TODO: add modulation sources (mod wheel, etc)
|
||||
uint32_t pwmd = (ctrls->values_[kControllerModWheel] * 0.7874) * (1 << 24); |
||||
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23)); |
||||
|
||||
pitchmod += (((int64_t)pwmd) * (int64_t)senslfo) >> 39; |
||||
pitchmod += (((int64_t)pmd) * (int64_t)senslfo) >> 39; |
||||
|
||||
int pitchbend = ctrls->values_[kControllerPitch]; |
||||
int32_t pb = (pitchbend - 0x2000); |
||||
|
||||
if ( pb != 0 ) { |
||||
if ( ctrls->values_[kControllerPitchStep] == 0 ) { |
||||
pb = ((float)(pb << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0; |
||||
} else { |
||||
int stp = 12 / ctrls->values_[kControllerPitchStep]; |
||||
pb = pb * stp / 8191; |
||||
pb = (pb * (8191/stp)) << 11; |
||||
} |
||||
} |
||||
|
||||
pitchmod += pb; |
||||
for (int op = 0; op < 6; op++) { |
||||
params_[op].gain[0] = params_[op].gain[1]; |
||||
int32_t level = env_[op].getsample(); |
||||
int32_t gain = Exp2::lookup(level - (14 * (1 << 24))); |
||||
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
|
||||
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchmod); |
||||
params_[op].gain[1] = gain; |
||||
} |
||||
core_.compute(buf, params_, algorithm_, fb_buf_, fb_shift_); |
||||
} |
||||
|
||||
void Dx7Note::keyup() { |
||||
for (int op = 0; op < 6; op++) { |
||||
env_[op].keydown(false); |
||||
pitchenv_.keydown(false); |
||||
} |
||||
} |
||||
|
||||
void Dx7Note::update(const char patch[156], int midinote) { |
||||
for (int op = 0; op < 6; op++) { |
||||
int off = op * 21; |
||||
int mode = patch[off + 17]; |
||||
int coarse = patch[off + 18]; |
||||
int fine = patch[off + 19]; |
||||
int detune = patch[off + 20]; |
||||
basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune); |
||||
} |
||||
algorithm_ = patch[134]; |
||||
int feedback = patch[135]; |
||||
fb_shift_ = feedback != 0 ? 8 - feedback : 16; |
||||
pitchmoddepth_ = (patch[139] * 165) >> 6; |
||||
pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; |
||||
} |
||||
|
||||
void Dx7Note::peekVoiceStatus(VoiceStatus &status) { |
||||
for(int i=0;i<6;i++) { |
||||
status.amp[i] = params_[i].gain[1]; |
||||
env_[i].getPosition(&status.ampStep[i]); |
||||
} |
||||
pitchenv_.getPosition(&status.pitchStep); |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifdef VERBOSE |
||||
#include <iostream> |
||||
using namespace std; |
||||
#endif |
||||
#include <math.h> |
||||
#include "synth.h" |
||||
#include "freqlut.h" |
||||
#include "exp2.h" |
||||
#include "controllers.h" |
||||
#include "dx7note.h" |
||||
|
||||
void dexed_trace(const char *source, const char *fmt, ...); |
||||
|
||||
#ifdef _MSC_VER |
||||
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__) |
||||
#else |
||||
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__) |
||||
#endif |
||||
|
||||
int32_t midinote_to_logfreq(int midinote) { |
||||
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
|
||||
const int step = (1 << 24) / 12; |
||||
return base + step * midinote; |
||||
} |
||||
|
||||
const int32_t coarsemul[] = { |
||||
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600, |
||||
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816, |
||||
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858, |
||||
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032, |
||||
81503396, 82323963, 83117622 |
||||
}; |
||||
|
||||
int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) { |
||||
// TODO: pitch randomization
|
||||
int32_t logfreq; |
||||
if (mode == 0) { |
||||
logfreq = midinote_to_logfreq(midinote); |
||||
logfreq += coarsemul[coarse & 31]; |
||||
if (fine) { |
||||
// (1 << 24) / log(2)
|
||||
logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5); |
||||
} |
||||
// This was measured at 7.213Hz per count at 9600Hz, but the exact
|
||||
// value is somewhat dependent on midinote. Close enough for now.
|
||||
logfreq += 12606 * (detune - 7); |
||||
} else { |
||||
// ((1 << 24) * log(10) / log(2) * .01) << 3
|
||||
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3; |
||||
logfreq += detune > 7 ? 13457 * (detune - 7) : 0; |
||||
} |
||||
return logfreq; |
||||
} |
||||
|
||||
const uint8_t velocity_data[64] = { |
||||
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163, |
||||
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202, |
||||
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232, |
||||
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251, |
||||
252, 253, 254 |
||||
}; |
||||
|
||||
// See "velocity" section of notes. Returns velocity delta in microsteps.
|
||||
int ScaleVelocity(int velocity, int sensitivity) { |
||||
int clamped_vel = max(0, min(127, velocity)); |
||||
int vel_value = velocity_data[clamped_vel >> 1] - 239; |
||||
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4; |
||||
return scaled_vel; |
||||
} |
||||
|
||||
int ScaleRate(int midinote, int sensitivity) { |
||||
int x = min(31, max(0, midinote / 3 - 7)); |
||||
int qratedelta = (sensitivity * x) >> 3; |
||||
#ifdef SUPER_PRECISE |
||||
int rem = x & 7; |
||||
if (sensitivity == 3 && rem == 3) { |
||||
qratedelta -= 1; |
||||
} else if (sensitivity == 7 && rem > 0 && rem < 4) { |
||||
qratedelta += 1; |
||||
} |
||||
#endif |
||||
return qratedelta; |
||||
} |
||||
|
||||
const uint8_t exp_scale_data[] = { |
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66, |
||||
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250 |
||||
}; |
||||
|
||||
int ScaleCurve(int group, int depth, int curve) { |
||||
int scale; |
||||
if (curve == 0 || curve == 3) { |
||||
// linear
|
||||
scale = (group * depth * 329) >> 12; |
||||
} else { |
||||
// exponential
|
||||
int n_scale_data = sizeof(exp_scale_data); |
||||
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)]; |
||||
scale = (raw_exp * depth * 329) >> 15; |
||||
} |
||||
if (curve < 2) { |
||||
scale = -scale; |
||||
} |
||||
return scale; |
||||
} |
||||
|
||||
int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth, |
||||
int left_curve, int right_curve) { |
||||
int offset = midinote - break_pt - 17; |
||||
if (offset >= 0) { |
||||
return ScaleCurve(offset / 3, right_depth, right_curve); |
||||
} else { |
||||
return ScaleCurve((-offset) / 3, left_depth, left_curve); |
||||
} |
||||
} |
||||
|
||||
static const uint8_t pitchmodsenstab[] = { |
||||
0, 10, 20, 33, 55, 92, 153, 255 |
||||
}; |
||||
|
||||
// PG: we need to find the real values
|
||||
static const uint8_t ampmodsenstab[] = { |
||||
0, 33, 153, 255 |
||||
}; |
||||
|
||||
void Dx7Note::init(const char patch[156], int midinote, int velocity) { |
||||
int rates[4]; |
||||
int levels[4]; |
||||
for (int op = 0; op < 6; op++) { |
||||
int off = op * 21; |
||||
for (int i = 0; i < 4; i++) { |
||||
rates[i] = patch[off + i]; |
||||
levels[i] = patch[off + 4 + i]; |
||||
} |
||||
int outlevel = patch[off + 16]; |
||||
outlevel = Env::scaleoutlevel(outlevel); |
||||
#ifdef VERBOSE |
||||
for (int j = 8; j < 12; j++) { |
||||
cout << (int)patch[off + j] << " "; |
||||
} |
||||
#endif |
||||
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9], |
||||
patch[off + 10], patch[off + 11], patch[off + 12]); |
||||
outlevel += level_scaling; |
||||
outlevel = min(127, outlevel); |
||||
#ifdef VERBOSE |
||||
cout << op << ": " << level_scaling << " " << outlevel << endl; |
||||
#endif |
||||
outlevel = outlevel << 5; |
||||
outlevel += ScaleVelocity(velocity, patch[off + 15]); |
||||
outlevel = max(0, outlevel); |
||||
int rate_scaling = ScaleRate(midinote, patch[off + 13]); |
||||
env_[op].init(rates, levels, outlevel, rate_scaling); |
||||
|
||||
int mode = patch[off + 17]; |
||||
int coarse = patch[off + 18]; |
||||
int fine = patch[off + 19]; |
||||
int detune = patch[off + 20]; |
||||
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune); |
||||
basepitch_[op] = freq; |
||||
// cout << op << " freq: " << freq << endl;
|
||||
params_[op].phase = 0; |
||||
params_[op].gain[1] = 0; |
||||
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; |
||||
|
||||
TRACE("operator set: %d %d", op, ampmodsens_[op]); |
||||
} |
||||
for (int i = 0; i < 4; i++) { |
||||
rates[i] = patch[126 + i]; |
||||
levels[i] = patch[130 + i]; |
||||
} |
||||
pitchenv_.set(rates, levels); |
||||
algorithm_ = patch[134]; |
||||
int feedback = patch[135]; |
||||
fb_shift_ = feedback != 0 ? 8 - feedback : 16; |
||||
pitchmoddepth_ = (patch[139] * 165) >> 6; |
||||
pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; |
||||
ampmoddepth_ = (patch[140] * 165) >> 6; |
||||
} |
||||
|
||||
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, |
||||
const Controllers *ctrls) { |
||||
int32_t pitchmod = pitchenv_.getsample(); |
||||
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
|
||||
// TODO(PG) : make this integer friendly
|
||||
uint32_t pwmd = (ctrls->values_[kControllerModWheel] * 0.7874) * (1 << 24); |
||||
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23)); |
||||
uint32_t amd = ampmoddepth_ * lfo_delay; // Q32 :D
|
||||
|
||||
pitchmod += (((int64_t)pwmd) * (int64_t)senslfo) >> 39; |
||||
pitchmod += (((int64_t)pmd) * (int64_t)senslfo) >> 39; |
||||
|
||||
int pitchbend = ctrls->values_[kControllerPitch]; |
||||
int32_t pb = (pitchbend - 0x2000); |
||||
|
||||
if ( pb != 0 ) { |
||||
if ( ctrls->values_[kControllerPitchStep] == 0 ) { |
||||
pb = ((float)(pb << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0; |
||||
} else { |
||||
int stp = 12 / ctrls->values_[kControllerPitchStep]; |
||||
pb = pb * stp / 8191; |
||||
pb = (pb * (8191/stp)) << 11; |
||||
} |
||||
} |
||||
|
||||
pitchmod += pb; |
||||
for (int op = 0; op < 6; op++) { |
||||
params_[op].gain[0] = params_[op].gain[1]; |
||||
|
||||
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
|
||||
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchmod); |
||||
|
||||
int32_t level = env_[op].getsample(); |
||||
if ( ampmodsens_[op] != 0 ) { |
||||
uint32_t sensamp = ampmodsens_[op] * (lfo_val - (1 << 23)); |
||||
uint32_t amd_level = (((int64_t)amd) * (int64_t)sensamp) >> 40; |
||||
level -= amd_level; |
||||
} |
||||
int32_t gain = Exp2::lookup(level - (14 * (1 << 24))); |
||||
params_[op].gain[1] = gain; |
||||
} |
||||
core_.compute(buf, params_, algorithm_, fb_buf_, fb_shift_, ctrls); |
||||
} |
||||
|
||||
void Dx7Note::keyup() { |
||||
for (int op = 0; op < 6; op++) { |
||||
env_[op].keydown(false); |
||||
pitchenv_.keydown(false); |
||||
} |
||||
} |
||||
|
||||
void Dx7Note::update(const char patch[156], int midinote) { |
||||
for (int op = 0; op < 6; op++) { |
||||
int off = op * 21; |
||||
int mode = patch[off + 17]; |
||||
int coarse = patch[off + 18]; |
||||
int fine = patch[off + 19]; |
||||
int detune = patch[off + 20]; |
||||
basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune); |
||||
} |
||||
algorithm_ = patch[134]; |
||||
int feedback = patch[135]; |
||||
fb_shift_ = feedback != 0 ? 8 - feedback : 16; |
||||
pitchmoddepth_ = (patch[139] * 165) >> 6; |
||||
pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; |
||||
} |
||||
|
||||
void Dx7Note::peekVoiceStatus(VoiceStatus &status) { |
||||
for(int i=0;i<6;i++) { |
||||
status.amp[i] = params_[i].gain[1]; |
||||
env_[i].getPosition(&status.ampStep[i]); |
||||
} |
||||
pitchenv_.getPosition(&status.pitchStep); |
||||
} |
||||
|
||||
|
||||
|
@ -1,73 +1,74 @@ |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifndef SYNTH_DX7NOTE_H_ |
||||
#define SYNTH_DX7NOTE_H_ |
||||
|
||||
// This is the logic to put together a note from the MIDI description
|
||||
// and run the low-level modules.
|
||||
|
||||
// It will continue to evolve a bit, as note-stealing logic, scaling,
|
||||
// and real-time control of parameters live here.
|
||||
|
||||
#include "env.h" |
||||
#include "pitchenv.h" |
||||
#include "fm_core.h" |
||||
|
||||
struct VoiceStatus { |
||||
uint32_t amp[6]; |
||||
char ampStep[6]; |
||||
char pitchStep; |
||||
}; |
||||
|
||||
class Dx7Note { |
||||
public: |
||||
void init(const char patch[156], int midinote, int velocity); |
||||
|
||||
// Note: this _adds_ to the buffer. Interesting question whether it's
|
||||
// worth it...
|
||||
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, |
||||
const Controllers *ctrls); |
||||
|
||||
void keyup(); |
||||
|
||||
// TODO: parameter changes
|
||||
|
||||
// TODO: some way of indicating end-of-note. Maybe should be a return
|
||||
// value from the compute method? (Having a count return from keyup
|
||||
// is also tempting, but if there's a dynamic parameter change after
|
||||
// keyup, that won't work.
|
||||
|
||||
// PG:add the update
|
||||
void update(const char patch[156], int midinote); |
||||
void peekVoiceStatus(VoiceStatus &status); |
||||
|
||||
private: |
||||
FmCore core_; |
||||
Env env_[6]; |
||||
FmOpParams params_[6]; |
||||
PitchEnv pitchenv_; |
||||
int32_t basepitch_[6]; |
||||
int32_t fb_buf_[2]; |
||||
int32_t fb_shift_; |
||||
|
||||
int ampmoddepth_; |
||||
int algorithm_; |
||||
int pitchmoddepth_; |
||||
int pitchmodsens_; |
||||
}; |
||||
|
||||
#endif // SYNTH_DX7NOTE_H_
|
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifndef SYNTH_DX7NOTE_H_ |
||||
#define SYNTH_DX7NOTE_H_ |
||||
|
||||
// This is the logic to put together a note from the MIDI description
|
||||
// and run the low-level modules.
|
||||
|
||||
// It will continue to evolve a bit, as note-stealing logic, scaling,
|
||||
// and real-time control of parameters live here.
|
||||
|
||||
#include "env.h" |
||||
#include "pitchenv.h" |
||||
#include "fm_core.h" |
||||
|
||||
struct VoiceStatus { |
||||
uint32_t amp[6]; |
||||
char ampStep[6]; |
||||
char pitchStep; |
||||
}; |
||||
|
||||
class Dx7Note { |
||||
public: |
||||
void init(const char patch[156], int midinote, int velocity); |
||||
|
||||
// Note: this _adds_ to the buffer. Interesting question whether it's
|
||||
// worth it...
|
||||
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, |
||||
const Controllers *ctrls); |
||||
|
||||
void keyup(); |
||||
|
||||
// TODO: parameter changes
|
||||
|
||||
// TODO: some way of indicating end-of-note. Maybe should be a return
|
||||
// value from the compute method? (Having a count return from keyup
|
||||
// is also tempting, but if there's a dynamic parameter change after
|
||||
// keyup, that won't work.
|
||||
|
||||
// PG:add the update
|
||||
void update(const char patch[156], int midinote); |
||||
void peekVoiceStatus(VoiceStatus &status);
|
||||
|
||||
private: |
||||
FmCore core_; |
||||
Env env_[6]; |
||||
FmOpParams params_[6]; |
||||
PitchEnv pitchenv_; |
||||
int32_t basepitch_[6]; |
||||
int32_t fb_buf_[2]; |
||||
int32_t fb_shift_; |
||||
int32_t ampmodsens_[6]; |
||||
|
||||
int ampmoddepth_; |
||||
int algorithm_; |
||||
int pitchmoddepth_; |
||||
int pitchmodsens_; |
||||
}; |
||||
|
||||
#endif // SYNTH_DX7NOTE_H_
|
||||
|
@ -1,151 +1,152 @@ |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifdef VERBOSE |
||||
#include <iostream> |
||||
#endif |
||||
|
||||
#include "synth.h" |
||||
#include "fm_op_kernel.h" |
||||
#include "fm_core.h" |
||||
|
||||
using namespace std; |
||||
|
||||
struct FmOperatorInfo { |
||||
int in; |
||||
int out; |
||||
}; |
||||
|
||||
enum FmOperatorFlags { |
||||
OUT_BUS_ONE = 1 << 0, |
||||
OUT_BUS_TWO = 1 << 1, |
||||
OUT_BUS_ADD = 1 << 2, |
||||
IN_BUS_ONE = 1 << 4, |
||||
IN_BUS_TWO = 1 << 5, |
||||
FB_IN = 1 << 6, |
||||
FB_OUT = 1 << 7 |
||||
}; |
||||
|
||||
struct FmAlgorithm { |
||||
int ops[6]; |
||||
}; |
||||
|
||||
const FmAlgorithm algorithms[32] = { |
||||
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
|
||||
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
|
||||
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
|
||||
{ { 0x41, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
|
||||
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
|
||||
{ { 0x41, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
|
||||
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
|
||||
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
|
||||
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
|
||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10
|
||||
{ { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11
|
||||
{ { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12
|
||||
{ { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13
|
||||
{ { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14
|
||||
{ { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15
|
||||
{ { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16
|
||||
{ { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17
|
||||
{ { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18
|
||||
{ { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19
|
||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20
|
||||
{ { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21
|
||||
{ { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22
|
||||
{ { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23
|
||||
{ { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24
|
||||
{ { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25
|
||||
{ { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26
|
||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27
|
||||
{ { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28
|
||||
{ { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29
|
||||
{ { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30
|
||||
{ { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31
|
||||
{ { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32
|
||||
}; |
||||
|
||||
int n_out(const FmAlgorithm &alg) { |
||||
int count = 0; |
||||
for (int i = 0; i < 6; i++) { |
||||
if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
void FmCore::dump() { |
||||
#ifdef VERBOSE |
||||
for (int i = 0; i < 32; i++) { |
||||
cout << (i + 1) << ":"; |
||||
const FmAlgorithm &alg = algorithms[i]; |
||||
for (int j = 0; j < 6; j++) { |
||||
int flags = alg.ops[j]; |
||||
cout << " "; |
||||
if (flags & FB_IN) cout << "["; |
||||
cout << (flags & IN_BUS_ONE ? "1" : flags & IN_BUS_TWO ? "2" : "0") << "->"; |
||||
cout << (flags & OUT_BUS_ONE ? "1" : flags & OUT_BUS_TWO ? "2" : "0"); |
||||
if (flags & OUT_BUS_ADD) cout << "+"; |
||||
//cout << alg.ops[j].in << "->" << alg.ops[j].out;
|
||||
if (flags & FB_OUT) cout << "]"; |
||||
} |
||||
cout << " " << n_out(alg); |
||||
cout << endl; |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm, |
||||
int32_t *fb_buf, int feedback_shift) { |
||||
const int kLevelThresh = 1120; |
||||
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 ¶m = 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[0]; |
||||
int32_t gain2 = param.gain[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;
|
||||
FmOpKernel::compute_fb(outptr, param.phase, param.freq, |
||||
gain1, gain2, |
||||
fb_buf, feedback_shift, add); |
||||
} else { |
||||
// cout << op << " pure " << inbus << outbus << add << endl;
|
||||
FmOpKernel::compute_pure(outptr, param.phase, param.freq, |
||||
gain1, gain2, add); |
||||
} |
||||
} else { |
||||
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
|
||||
FmOpKernel::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; |
||||
} |
||||
} |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifdef VERBOSE |
||||
#include <iostream> |
||||
#endif |
||||
|
||||
#include "synth.h" |
||||
#include "fm_op_kernel.h" |
||||
#include "fm_core.h" |
||||
|
||||
|
||||
using namespace std; |
||||
|
||||
struct FmOperatorInfo { |
||||
int in; |
||||
int out; |
||||
}; |
||||
|
||||
enum FmOperatorFlags { |
||||
OUT_BUS_ONE = 1 << 0, |
||||
OUT_BUS_TWO = 1 << 1, |
||||
OUT_BUS_ADD = 1 << 2, |
||||
IN_BUS_ONE = 1 << 4, |
||||
IN_BUS_TWO = 1 << 5, |
||||
FB_IN = 1 << 6, |
||||
FB_OUT = 1 << 7 |
||||
}; |
||||
|
||||
struct FmAlgorithm { |
||||
int ops[6]; |
||||
}; |
||||
|
||||
const FmAlgorithm algorithms[32] = { |
||||
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
|
||||
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
|
||||
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
|
||||
{ { 0x41, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
|
||||
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
|
||||
{ { 0x41, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
|
||||
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
|
||||
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
|
||||
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
|
||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10
|
||||
{ { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11
|
||||
{ { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12
|
||||
{ { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13
|
||||
{ { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14
|
||||
{ { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15
|
||||
{ { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16
|
||||
{ { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17
|
||||
{ { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18
|
||||
{ { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19
|
||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20
|
||||
{ { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21
|
||||
{ { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22
|
||||
{ { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23
|
||||
{ { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24
|
||||
{ { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25
|
||||
{ { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26
|
||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27
|
||||
{ { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28
|
||||
{ { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29
|
||||
{ { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30
|
||||
{ { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31
|
||||
{ { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32
|
||||
}; |
||||
|
||||
int n_out(const FmAlgorithm &alg) { |
||||
int count = 0; |
||||
for (int i = 0; i < 6; i++) { |
||||
if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
void FmCore::dump() { |
||||
#ifdef VERBOSE |
||||
for (int i = 0; i < 32; i++) { |
||||
cout << (i + 1) << ":"; |
||||
const FmAlgorithm &alg = algorithms[i]; |
||||
for (int j = 0; j < 6; j++) { |
||||
int flags = alg.ops[j]; |
||||
cout << " "; |
||||
if (flags & FB_IN) cout << "["; |
||||
cout << (flags & IN_BUS_ONE ? "1" : flags & IN_BUS_TWO ? "2" : "0") << "->"; |
||||
cout << (flags & OUT_BUS_ONE ? "1" : flags & OUT_BUS_TWO ? "2" : "0"); |
||||
if (flags & OUT_BUS_ADD) cout << "+"; |
||||
//cout << alg.ops[j].in << "->" << alg.ops[j].out;
|
||||
if (flags & FB_OUT) cout << "]"; |
||||
} |
||||
cout << " " << n_out(alg); |
||||
cout << endl; |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm, |
||||
int32_t *fb_buf, int feedback_shift, const Controllers *controllers) { |
||||
const int kLevelThresh = 1120; |
||||
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 ¶m = 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[0]; |
||||
int32_t gain2 = param.gain[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;
|
||||
FmOpKernel::compute_fb(outptr, param.phase, param.freq, |
||||
gain1, gain2, |
||||
fb_buf, feedback_shift, add, controllers); |
||||
} else { |
||||
// cout << op << " pure " << inbus << outbus << add << endl;
|
||||
FmOpKernel::compute_pure(outptr, param.phase, param.freq, |
||||
gain1, gain2, add, controllers); |
||||
} |
||||
} else { |
||||
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
|
||||
FmOpKernel::compute(outptr, buf_[inbus - 1].get(), |
||||
param.phase, param.freq, gain1, gain2, add, controllers); |
||||
} |
||||
has_contents[outbus] = true; |
||||
} else if (!add) { |
||||
has_contents[outbus] = false; |
||||
} |
||||
param.phase += param.freq << LG_N; |
||||
} |
||||
} |
@ -1,38 +1,40 @@ |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifndef __FM_CORE_H |
||||
#define __FM_CORE_H |
||||
|
||||
#include "aligned_buf.h" |
||||
#include "synth.h" |
||||
|
||||
struct FmOpParams { |
||||
int32_t gain[2]; |
||||
int32_t freq; |
||||
int32_t phase; |
||||
}; |
||||
|
||||
class FmCore { |
||||
public: |
||||
static void dump(); |
||||
void compute(int32_t *output, FmOpParams *params, int algorithm, |
||||
int32_t *fb_buf, int32_t feedback_gain); |
||||
private: |
||||
AlignedBuf<int32_t, N>buf_[2]; |
||||
}; |
||||
|
||||
#endif // __FM_CORE_H
|
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifndef __FM_CORE_H |
||||
#define __FM_CORE_H |
||||
|
||||
#include "aligned_buf.h" |
||||
#include "fm_op_kernel.h" |
||||
#include "synth.h" |
||||
#include "controllers.h" |
||||
|
||||
struct FmOpParams { |
||||
int32_t gain[2]; |
||||
int32_t freq; |
||||
int32_t phase; |
||||
}; |
||||
|
||||
class FmCore { |
||||
public: |
||||
static void dump(); |
||||
void compute(int32_t *output, FmOpParams *params, int algorithm, |
||||
int32_t *fb_buf, int32_t feedback_gain, const Controllers *controller); |
||||
private: |
||||
AlignedBuf<int32_t, N>buf_[2]; |
||||
}; |
||||
|
||||
#endif // __FM_CORE_H
|
||||
|
@ -1,274 +1,299 @@ |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 <math.h> |
||||
|
||||
#ifdef HAVE_NEON |
||||
#include <cpu-features.h> |
||||
#endif |
||||
|
||||
#include "synth.h" |
||||
|
||||
#include "sin.h" |
||||
#include "fm_op_kernel.h" |
||||
|
||||
#ifdef HAVE_NEON |
||||
static bool hasNeon() { |
||||
return true; |
||||
return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; |
||||
} |
||||
|
||||
extern "C" |
||||
void neon_fm_kernel(const int *in, const int *busin, int *out, int count, |
||||
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain); |
||||
|
||||
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0}; |
||||
|
||||
#else |
||||
static bool hasNeon() { |
||||
return false; |
||||
} |
||||
#endif |
||||
|
||||
void FmOpKernel::compute(int32_t *output, const int32_t *input, |
||||
int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
if (hasNeon()) { |
||||
#ifdef HAVE_NEON |
||||
neon_fm_kernel(input, add ? output : zeros, output, N, |
||||
phase0, freq, gain, dgain); |
||||
#endif |
||||
} else { |
||||
if (add) { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase + input[i]); |
||||
output[i] += ((int64_t)y * (int64_t)gain) >> 24; |
||||
phase += freq; |
||||
} |
||||
} else { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase + input[i]); |
||||
output[i] = ((int64_t)y * (int64_t)gain) >> 24; |
||||
phase += freq; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#if 1 |
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
if (hasNeon()) { |
||||
#ifdef HAVE_NEON |
||||
neon_fm_kernel(zeros, add ? output : zeros, output, N, |
||||
phase0, freq, gain, dgain); |
||||
#endif |
||||
} else { |
||||
if (add) { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase); |
||||
output[i] += ((int64_t)y * (int64_t)gain) >> 24; |
||||
phase += freq; |
||||
} |
||||
} else { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase); |
||||
output[i] = ((int64_t)y * (int64_t)gain) >> 24; |
||||
phase += freq; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#define noDOUBLE_ACCURACY |
||||
#define HIGH_ACCURACY |
||||
|
||||
// Experimental sine wave generators below
|
||||
#if 0 |
||||
// Results: accuracy 64.3 mean, 170 worst case
|
||||
// high accuracy: 5.0 mean, 49 worst case
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = Sin::compute10(phase << 6); |
||||
u = ((int64_t)u * gain) >> 30; |
||||
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
|
||||
v = ((int64_t)v * gain) >> 30; |
||||
int32_t s = Sin::compute10(freq << 6); |
||||
int32_t c = Sin::compute10((freq << 6) + (1 << 28)); |
||||
#else |
||||
int32_t u = Sin::compute(phase); |
||||
u = ((int64_t)u * gain) >> 24; |
||||
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
|
||||
v = ((int64_t)v * gain) >> 24; |
||||
int32_t s = Sin::compute(freq) << 6; |
||||
int32_t c = Sin::compute(freq + (1 << 22)) << 6; |
||||
#endif |
||||
for (int i = 0; i < N; i++) { |
||||
output[i] = u; |
||||
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; |
||||
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; |
||||
v = t; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if 0 |
||||
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
|
||||
// for freq < 0.25, 275.2 mean, 716 worst
|
||||
// high accuracy: 57.4 mean, 7559 worst
|
||||
// freq < 0.25: 17.9 mean, 78 worst
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5); |
||||
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5); |
||||
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5); |
||||
#else |
||||
int32_t u = Sin::compute(phase); |
||||
u = ((int64_t)u * gain) >> 24; |
||||
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
||||
v = ((int64_t)v * gain) >> 24; |
||||
int32_t a = Sin::compute(freq >> 1) << 1; |
||||
#endif |
||||
for (int i = 0; i < N; i++) { |
||||
output[i] = u; |
||||
v -= ((int64_t)a * (int64_t)u) >> 24; |
||||
u += ((int64_t)a * (int64_t)v) >> 24; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if 0 |
||||
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
|
||||
// with double accuracy initialization: mean 1.55, worst 58 (near freq = 0)
|
||||
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef DOUBLE_ACCURACY |
||||
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5); |
||||
double a_d = sin(freq * (M_PI / (1 << 24))); |
||||
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) * |
||||
(M_PI / (1 << 23))) + 0.5); |
||||
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5); |
||||
#else |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = Sin::compute10(phase << 6); |
||||
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5)); |
||||
int32_t a = Sin::compute10(freq << 5); |
||||
v = ((int64_t)v * (int64_t)a) >> 29; |
||||
int32_t aa = ((int64_t)a * (int64_t)a) >> 29; |
||||
#else |
||||
int32_t u = Sin::compute(phase) << 6; |
||||
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
||||
int32_t a = Sin::compute(freq >> 1); |
||||
v = ((int64_t)v * (int64_t)a) >> 17; |
||||
int32_t aa = ((int64_t)a * (int64_t)a) >> 17; |
||||
#endif |
||||
#endif |
||||
|
||||
if (aa < 0) aa = (1 << 31) - 1; |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
||||
v -= ((int64_t)aa * (int64_t)u) >> 29; |
||||
u += v; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if 0 |
||||
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
|
||||
// high accuracy 2.9 mean, 143 worst
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = Sin::compute10(phase << 6); |
||||
int32_t lastu = Sin::compute10((phase - freq) << 6); |
||||
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1; |
||||
#else |
||||
int32_t u = Sin::compute(phase) << 6; |
||||
int32_t lastu = Sin::compute(phase - freq) << 6; |
||||
int32_t a = Sin::compute(freq + (1 << 22)) << 7; |
||||
#endif |
||||
if (a < 0 && freq < 256) a = (1 << 31) - 1; |
||||
if (a > 0 && freq > 0x7fff00) a = -(1 << 31); |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
||||
//output[i] = u;
|
||||
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu; |
||||
lastu = u; |
||||
u = newu; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, |
||||
int32_t *fb_buf, int fb_shift, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
int32_t y0 = fb_buf[0]; |
||||
int32_t y = fb_buf[1]; |
||||
if (add) { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
||||
y0 = y; |
||||
y = Sin::lookup(phase + scaled_fb); |
||||
y = ((int64_t)y * (int64_t)gain) >> 24; |
||||
output[i] += y; |
||||
phase += freq; |
||||
} |
||||
} else { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
||||
y0 = y; |
||||
y = Sin::lookup(phase + scaled_fb); |
||||
y = ((int64_t)y * (int64_t)gain) >> 24; |
||||
output[i] = y; |
||||
phase += freq; |
||||
} |
||||
} |
||||
fb_buf[0] = y0; |
||||
fb_buf[1] = y; |
||||
} |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 <math.h> |
||||
|
||||
#include <cstdlib> |
||||
|
||||
#ifdef HAVE_NEON |
||||
#include <cpu-features.h> |
||||
#endif |
||||
|
||||
#include "synth.h" |
||||
|
||||
#include "sin.h" |
||||
#include "fm_op_kernel.h" |
||||
|
||||
|
||||
uint32_t restdither() { |
||||
return rand() & 0x3FFF; |
||||
return 0; |
||||
|
||||
} |
||||
|
||||
#ifdef HAVE_NEONx |
||||
static bool hasNeon() { |
||||
return true; |
||||
return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; |
||||
} |
||||
|
||||
extern "C" |
||||
void neon_fm_kernel(const int *in, const int *busin, int *out, int count, |
||||
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain); |
||||
|
||||
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0}; |
||||
|
||||
#else |
||||
static bool hasNeon() { |
||||
return false; |
||||
} |
||||
#endif |
||||
|
||||
//#define BIT8 0xFFFFFF00
|
||||
|
||||
void FmOpKernel::compute(int32_t *output, const int32_t *input, |
||||
int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
if (hasNeon()) { |
||||
#ifdef HAVE_NEON |
||||
neon_fm_kernel(input, add ? output : zeros, output, N, |
||||
phase0, freq, gain, dgain); |
||||
#endif |
||||
} else { |
||||
if (add) { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase + input[i]); |
||||
y &= controllers->sinBitFilter; |
||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
||||
output[i] += y1; |
||||
phase += freq; |
||||
} |
||||
} else { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase + input[i]); |
||||
y &= controllers->sinBitFilter; |
||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
||||
output[i] = y1; |
||||
phase += freq; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
if (hasNeon()) { |
||||
#ifdef HAVE_NEON |
||||
neon_fm_kernel(zeros, add ? output : zeros, output, N, |
||||
phase0, freq, gain, dgain); |
||||
#endif |
||||
} else { |
||||
if (add) { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase); |
||||
y &= controllers->sinBitFilter; |
||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
||||
output[i] += y1; |
||||
phase += freq; |
||||
} |
||||
} else { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t y = Sin::lookup(phase); |
||||
y &= controllers->sinBitFilter; |
||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
|
||||
output[i] = y1; |
||||
phase += freq; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#define noDOUBLE_ACCURACY |
||||
#define HIGH_ACCURACY |
||||
|
||||
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, |
||||
int32_t *fb_buf, int fb_shift, bool add, const Controllers *controllers) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
int32_t y0 = fb_buf[0]; |
||||
int32_t y = fb_buf[1]; |
||||
if (add) { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
||||
y0 = y; |
||||
y = Sin::lookup(phase + scaled_fb); |
||||
y &= controllers->sinBitFilter; |
||||
y = ((int64_t)y * (int64_t)gain) >> 24; |
||||
output[i] += y; |
||||
phase += freq; |
||||
} |
||||
} else { |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
||||
y0 = y; |
||||
y = Sin::lookup(phase + scaled_fb); |
||||
y &= controllers->sinBitFilter; |
||||
y = ((int64_t)y * (int64_t)gain) >> 24; |
||||
output[i] = y; |
||||
phase += freq; |
||||
} |
||||
} |
||||
fb_buf[0] = y0; |
||||
fb_buf[1] = y; |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Experimental sine wave generators below
|
||||
#if 0 |
||||
// Results: accuracy 64.3 mean, 170 worst case
|
||||
// high accuracy: 5.0 mean, 49 worst case
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = Sin::compute10(phase << 6); |
||||
u = ((int64_t)u * gain) >> 30; |
||||
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
|
||||
v = ((int64_t)v * gain) >> 30; |
||||
int32_t s = Sin::compute10(freq << 6); |
||||
int32_t c = Sin::compute10((freq << 6) + (1 << 28)); |
||||
#else |
||||
int32_t u = Sin::compute(phase); |
||||
u = ((int64_t)u * gain) >> 24; |
||||
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
|
||||
v = ((int64_t)v * gain) >> 24; |
||||
int32_t s = Sin::compute(freq) << 6; |
||||
int32_t c = Sin::compute(freq + (1 << 22)) << 6; |
||||
#endif |
||||
for (int i = 0; i < N; i++) { |
||||
output[i] = u; |
||||
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; |
||||
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; |
||||
v = t; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if 0 |
||||
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
|
||||
// for freq < 0.25, 275.2 mean, 716 worst
|
||||
// high accuracy: 57.4 mean, 7559 worst
|
||||
// freq < 0.25: 17.9 mean, 78 worst
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5); |
||||
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5); |
||||
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5); |
||||
#else |
||||
int32_t u = Sin::compute(phase); |
||||
u = ((int64_t)u * gain) >> 24; |
||||
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
||||
v = ((int64_t)v * gain) >> 24; |
||||
int32_t a = Sin::compute(freq >> 1) << 1; |
||||
#endif |
||||
for (int i = 0; i < N; i++) { |
||||
output[i] = u; |
||||
v -= ((int64_t)a * (int64_t)u) >> 24; |
||||
u += ((int64_t)a * (int64_t)v) >> 24; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if 0 |
||||
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
|
||||
// with double accuracy initialization: mean 1.55, worst 58 (near freq = 0)
|
||||
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef DOUBLE_ACCURACY |
||||
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5); |
||||
double a_d = sin(freq * (M_PI / (1 << 24))); |
||||
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) * |
||||
(M_PI / (1 << 23))) + 0.5); |
||||
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5); |
||||
#else |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = Sin::compute10(phase << 6); |
||||
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5)); |
||||
int32_t a = Sin::compute10(freq << 5); |
||||
v = ((int64_t)v * (int64_t)a) >> 29; |
||||
int32_t aa = ((int64_t)a * (int64_t)a) >> 29; |
||||
#else |
||||
int32_t u = Sin::compute(phase) << 6; |
||||
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
||||
int32_t a = Sin::compute(freq >> 1); |
||||
v = ((int64_t)v * (int64_t)a) >> 17; |
||||
int32_t aa = ((int64_t)a * (int64_t)a) >> 17; |
||||
#endif |
||||
#endif |
||||
|
||||
if (aa < 0) aa = (1 << 31) - 1; |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
||||
v -= ((int64_t)aa * (int64_t)u) >> 29; |
||||
u += v; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#if 0 |
||||
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
|
||||
// high accuracy 2.9 mean, 143 worst
|
||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add) { |
||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||
int32_t gain = gain1; |
||||
int32_t phase = phase0; |
||||
#ifdef HIGH_ACCURACY |
||||
int32_t u = Sin::compute10(phase << 6); |
||||
int32_t lastu = Sin::compute10((phase - freq) << 6); |
||||
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1; |
||||
#else |
||||
int32_t u = Sin::compute(phase) << 6; |
||||
int32_t lastu = Sin::compute(phase - freq) << 6; |
||||
int32_t a = Sin::compute(freq + (1 << 22)) << 7; |
||||
#endif |
||||
if (a < 0 && freq < 256) a = (1 << 31) - 1; |
||||
if (a > 0 && freq > 0x7fff00) a = -(1 << 31); |
||||
for (int i = 0; i < N; i++) { |
||||
gain += dgain; |
||||
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
||||
//output[i] = u;
|
||||
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu; |
||||
lastu = u; |
||||
u = newu; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
|
@ -1,35 +1,42 @@ |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
class FmOpKernel { |
||||
public: |
||||
// gain1 and gain2 represent linear step: gain for sample i is
|
||||
// gain1 + (1 + i) / 64 * (gain2 - gain1)
|
||||
|
||||
// This is the basic FM operator. No feedback.
|
||||
static void compute(int32_t *output, const int32_t *input, |
||||
int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add); |
||||
|
||||
// This is a sine generator, no feedback.
|
||||
static void compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add); |
||||
|
||||
// One op with feedback, no add.
|
||||
static void compute_fb(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, |
||||
int32_t *fb_buf, int fb_gain, bool add); |
||||
}; |
||||
/*
|
||||
* Copyright 2012 Google Inc. |
||||
*
|
||||
* 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 |
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#ifndef __FM_OP_KERNEL_H |
||||
#define __FM_OP_KERNEL_H |
||||
|
||||
#include "controllers.h" |
||||
|
||||
class FmOpKernel { |
||||
public:
|
||||
// gain1 and gain2 represent linear step: gain for sample i is
|
||||
// gain1 + (1 + i) / 64 * (gain2 - gain1)
|
||||
|
||||
// This is the basic FM operator. No feedback.
|
||||
static void compute(int32_t *output, const int32_t *input, |
||||
int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers); |
||||
|
||||
// This is a sine generator, no feedback.
|
||||
static void compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers); |
||||
|
||||
// One op with feedback, no add.
|
||||
static void compute_fb(int32_t *output, int32_t phase0, int32_t freq, |
||||
int32_t gain1, int32_t gain2, |
||||
int32_t *fb_buf, int fb_gain, bool add, const Controllers *controllers); |
||||
}; |
||||
|
||||
#endif |
||||
|
Loading…
Reference in new issue