/// <summary> /// SID clocking with audio sampling - delta clocking picking nearest sample /// </summary> /// <param name="delta_t"></param> /// <param name="buf"></param> /// <param name="n"></param> /// <param name="interleave"></param> /// <returns></returns> protected int clock_fast(CycleCount delta_t, short[] buf, int n, int interleave) { int s = 0; for (; ;) { int next_sample_offset = sample_offset + cycles_per_sample + (1 << (FIXP_SHIFT - 1)); int delta_t_sample = next_sample_offset >> FIXP_SHIFT; if (delta_t_sample > delta_t.delta_t) { break; } if (s >= n) { return(s); } clock(delta_t_sample); delta_t.delta_t -= delta_t_sample; sample_offset = (next_sample_offset & FIXP_MASK) - (1 << (FIXP_SHIFT - 1)); buf[s++ *interleave] = (short)output(); } clock(delta_t.delta_t); sample_offset -= delta_t.delta_t << FIXP_SHIFT; delta_t.delta_t = 0; return(s); }
/// <summary> /// SID clocking with audio sampling - cycle based with linear sample interpolation. /// /// Here the chip is clocked every cycle. This yields higher quality sound /// since the samples are linearly interpolated, and since the external /// filter attenuates frequencies above 16kHz, thus reducing sampling noise /// </summary> /// <param name="delta_t"></param> /// <param name="buf"></param> /// <param name="n"></param> /// <param name="interleave"></param> /// <returns></returns> protected int clock_interpolate(CycleCount delta_t, short[] buf, int n, int interleave) { int s = 0; int i; for (; ;) { int next_sample_offset = sample_offset + cycles_per_sample; int delta_t_sample = next_sample_offset >> FIXP_SHIFT; if (delta_t_sample > delta_t.delta_t) { break; } if (s >= n) { return(s); } for (i = 0; i < delta_t_sample - 1; i++) { clock(); } if (i < delta_t_sample) { sample_prev = (short)output(); clock(); } delta_t.delta_t -= delta_t_sample; sample_offset = next_sample_offset & FIXP_MASK; short sample_now = (short)output(); buf[s++ *interleave] = (short)(sample_prev + (sample_offset * (sample_now - sample_prev) >> FIXP_SHIFT)); sample_prev = sample_now; } for (i = 0; i < delta_t.delta_t - 1; i++) { clock(); } if (i < delta_t.delta_t) { sample_prev = (short)output(); clock(); } sample_offset -= delta_t.delta_t << FIXP_SHIFT; delta_t.delta_t = 0; return(s); }
/// <summary> /// SID clocking with audio sampling /// </summary> /// <param name="delta_t"></param> /// <param name="buf"></param> /// <param name="n"></param> /// <param name="interleave"></param> /// <returns></returns> public int clock(CycleCount delta_t, short[] buf, int n, int interleave) { switch (sampling) { default: case SIDDefs.sampling_method.SAMPLE_FAST: return(clock_fast(delta_t, buf, n, interleave)); case SIDDefs.sampling_method.SAMPLE_INTERPOLATE: return(clock_interpolate(delta_t, buf, n, interleave)); case SIDDefs.sampling_method.SAMPLE_RESAMPLE_INTERPOLATE: return(clock_resample_interpolate(delta_t, buf, n, interleave)); case SIDDefs.sampling_method.SAMPLE_RESAMPLE_FAST: return(clock_resample_fast(delta_t, buf, n, interleave)); } }
/// <summary> /// SID clocking with audio sampling - cycle based with audio resampling /// </summary> /// <param name="delta_t"></param> /// <param name="buf"></param> /// <param name="n"></param> /// <param name="interleave"></param> /// <returns></returns> protected int clock_resample_fast(CycleCount delta_t, short[] buf, int n, int interleave) { int s = 0; for (; ;) { int next_sample_offset = sample_offset + cycles_per_sample; int delta_t_sample = next_sample_offset >> FIXP_SHIFT; if (delta_t_sample > delta_t.delta_t) { break; } if (s >= n) { return(s); } for (int i = 0; i < delta_t_sample; i++) { clock(); sample[sample_index] = sample[sample_index + RINGSIZE] = (short)output(); ++sample_index; sample_index &= 0x3fff; } delta_t.delta_t -= delta_t_sample; sample_offset = next_sample_offset & FIXP_MASK; int fir_offset = sample_offset * fir_RES >> FIXP_SHIFT; int fir_start = (fir_offset * fir_N); int sample_start = (sample_index - fir_N + RINGSIZE); // Convolution with filter impulse response. int v = 0; for (int j = 0; j < fir_N; j++) { v += sample[sample_start + j] * fir[fir_start + j]; } v >>= FIR_SHIFT; // Saturated arithmetics to guard against 16 bit sample overflow. int half = 1 << 15; if (v >= half) { v = half - 1; } else if (v < -half) { v = -half; } buf[s++ *interleave] = (short)v; } for (int i = 0; i < delta_t.delta_t; i++) { clock(); sample[sample_index] = sample[sample_index + RINGSIZE] = (short)output(); ++sample_index; sample_index &= 0x3fff; } sample_offset -= delta_t.delta_t << FIXP_SHIFT; delta_t.delta_t = 0; return(s); }
/// <summary> /// SID clocking with audio sampling - cycle based with audio resampling. /// /// This is the theoretically correct (and computationally intensive) audio /// sample generation. The samples are generated by resampling to the /// specified sampling frequency. The work rate is inversely proportional to /// the percentage of the bandwidth allocated to the filter transition band. /// /// This implementation is based on the paper "A Flexible Sampling-Rate /// Conversion Method", by J. O. Smith and P. Gosset, or rather on the /// expanded tutorial on the "Digital Audio Resampling Home Page": /// http://www-ccrma.stanford.edu/~jos/resample/ /// /// By building shifted FIR tables with samples according to the sampling /// frequency, this implementation dramatically reduces the computational /// effort in the filter convolutions, without any loss of accuracy. The /// filter convolutions are also vectorizable on current hardware. /// /// Further possible optimizations are: * An equiripple filter design could /// yield a lower filter order, see /// http://www.mwrf.com/Articles/ArticleID/7229/7229.html * The Convolution /// Theorem could be used to bring the complexity of convolution down from /// O(n*n) to O(n*log(n)) using the Fast Fourier Transform, see /// http://en.wikipedia.org/wiki/Convolution_theorem * Simply resampling in /// two steps can also yield computational savings, since the transition band /// will be wider in the first step and the required filter order is thus /// lower in this step. Laurent Ganier has found the optimal intermediate /// sampling frequency to be (via derivation of sum of two steps): 2 * /// pass_freq + sqrt [ 2 * pass_freq * orig_sample_freq * (dest_sample_freq - /// 2 * pass_freq) / dest_sample_freq ] /// </summary> /// <param name="delta_t"></param> /// <param name="buf"></param> /// <param name="n"></param> /// <param name="interleave"></param> /// <returns></returns> protected int clock_resample_interpolate(CycleCount delta_t, short[] buf, int n, int interleave) { int s = 0; for (; ;) { int next_sample_offset = sample_offset + cycles_per_sample; int delta_t_sample = next_sample_offset >> FIXP_SHIFT; if (delta_t_sample > delta_t.delta_t) { break; } if (s >= n) { return(s); } for (int i = 0; i < delta_t_sample; i++) { clock(); sample[sample_index] = sample[sample_index + RINGSIZE] = (short)output(); ++sample_index; sample_index &= 0x3fff; } delta_t.delta_t -= delta_t_sample; sample_offset = next_sample_offset & FIXP_MASK; int fir_offset = sample_offset * fir_RES >> FIXP_SHIFT; int fir_offset_rmd = sample_offset * fir_RES & FIXP_MASK; int fir_start = (fir_offset * fir_N); int sample_start = (sample_index - fir_N + RINGSIZE); // Convolution with filter impulse response. int v1 = 0; for (int j = 0; j < fir_N; j++) { v1 += sample[sample_start + j] * fir[fir_start + j]; } // Use next FIR table, wrap around to first FIR table using // previous sample. if (++fir_offset == fir_RES) { fir_offset = 0; --sample_start; } fir_start = (fir_offset * fir_N); // Convolution with filter impulse response. int v2 = 0; for (int j = 0; j < fir_N; j++) { v2 += sample[sample_start + j] * fir[fir_start + j]; } // Linear interpolation. // fir_offset_rmd is equal for all samples, it can thus be // factorized out: // sum(v1 + rmd*(v2 - v1)) = sum(v1) + rmd*(sum(v2) - sum(v1)) int v = v1 + (fir_offset_rmd * (v2 - v1) >> FIXP_SHIFT); v >>= FIR_SHIFT; // Saturated arithmetics to guard against 16 bit sample overflow. int half = 1 << 15; if (v >= half) { v = half - 1; } else if (v < -half) { v = -half; } buf[s++ *interleave] = (short)v; } for (int i = 0; i < delta_t.delta_t; i++) { clock(); sample[sample_index] = sample[sample_index + RINGSIZE] = (short)output(); ++sample_index; sample_index &= 0x3fff; } sample_offset -= delta_t.delta_t << FIXP_SHIFT; delta_t.delta_t = 0; return(s); }