// deserializing protected void LoadFromReader(BinaryReader reader) { int count; voice0 = new Voice(reader); voice1 = new Voice(reader); voice2 = new Voice(reader); voice0.wave.UpdateAfterLoad(voice0, voice1, voice2); voice1.wave.UpdateAfterLoad(voice0, voice1, voice2); voice2.wave.UpdateAfterLoad(voice0, voice1, voice2); filter = new Filter(reader); extfilt = new ExternalFilter(reader); bus_value = reader.ReadInt32(); bus_value_ttl = reader.ReadInt32(); clock_frequency = reader.ReadDouble(); ext_in = reader.ReadInt32(); sampling = (SIDDefs.sampling_method)reader.ReadInt16(); cycles_per_sample = reader.ReadInt32(); sample_offset = reader.ReadInt32(); sample_index = reader.ReadInt32(); sample_prev = reader.ReadInt16(); fir_N = reader.ReadInt32(); fir_RES = reader.ReadInt32(); count = reader.ReadInt32(); if (count == -1) { sample = null; } else { sample = new short[count]; for (int i = 0; i < count; i++) { sample[i] = reader.ReadInt16(); } } count = reader.ReadInt32(); if (count == -1) { fir = null; } else { fir = new short[count]; for (int i = 0; i < count; i++) { fir[i] = reader.ReadInt16(); } } }
/// <summary> /// Setting of SID sampling parameters. /// Use a clock freqency of 985248Hz for PAL C64, 1022730Hz for NTSC C64. The /// default end of passband frequency is pass_freq = 0.9*sample_freq/2 for /// sample frequencies up to ~ 44.1kHz, and 20kHz for higher sample /// frequencies. /// /// For resampling, the ratio between the clock frequency and the sample /// frequency is limited as follows: 125*clock_freq/sample_freq < 16384 E.g. /// provided a clock frequency of ~ 1MHz, the sample frequency can not be set /// lower than ~ 8kHz. A lower sample frequency would make the resampling /// code overfill its 16k sample ring buffer. /// /// The end of passband frequency is also limited: pass_freq <= /// 0.9*sample_freq/2 /// /// E.g. for a 44.1kHz sampling rate the end of passband frequency is limited /// to slightly below 20kHz. This constraint ensures that the FIR table is /// not overfilled. /// </summary> /// <param name="clock_freq"></param> /// <param name="method"></param> /// <param name="sample_freq"></param> /// <param name="pass_freq"></param> /// <param name="filter_scale"></param> /// <returns></returns> public bool set_sampling_parameters(double clock_freq, SIDDefs.sampling_method method, double sample_freq, double pass_freq, double filter_scale) { // Check resampling constraints if (method == SIDDefs.sampling_method.SAMPLE_RESAMPLE_INTERPOLATE || method == SIDDefs.sampling_method.SAMPLE_RESAMPLE_FAST) { // Check whether the sample ring buffer would overfill. if (FIR_N * clock_freq / sample_freq >= RINGSIZE) { return(false); } } // The default passband limit is 0.9*sample_freq/2 for sample // frequencies below ~ 44.1kHz, and 20kHz for higher sample // frequencies if (pass_freq < 0) { pass_freq = 20000; if (2 * pass_freq / sample_freq >= 0.9) { pass_freq = 0.9 * sample_freq / 2; } } // Check whether the FIR table would overfill else if (pass_freq > 0.9 * sample_freq / 2) { return(false); } // The filter scaling is only included to avoid clipping, so keep it sane. if (filter_scale < 0.9 || filter_scale > 1.0) { return(false); } // Set the external filter to the pass freq extfilt.set_sampling_parameter(pass_freq); clock_frequency = clock_freq; sampling = method; cycles_per_sample = (int)(clock_freq / sample_freq * (1 << FIXP_SHIFT) + 0.5); sample_offset = 0; sample_prev = 0; // FIR initialization is only necessary for resampling if (method != SIDDefs.sampling_method.SAMPLE_RESAMPLE_INTERPOLATE && method != SIDDefs.sampling_method.SAMPLE_RESAMPLE_FAST) { sample = null; fir = null; return(true); } double pi = 3.1415926535897932385; // 16 bits -> -96dB stopband attenuation double A = -20 * Math.Log10(1.0 / (1 << 16)); // A fraction of the bandwidth is allocated to the transition band, double dw = (1 - 2 * pass_freq / sample_freq) * pi; // The cutoff frequency is midway through the transition band. double wc = (2 * pass_freq / sample_freq + 1) * pi / 2; // For calculation of beta and N see the reference for the kaiserord // function in the MATLAB Signal Processing Toolbox: // http://www.mathworks.com/access/helpdesk/help/toolbox/signal/kaiserord.html double beta = 0.1102 * (A - 8.7); double I0beta = I0(beta); // The filter order will maximally be 124 with the current constraints. // N >= (96.33 - 7.95)/(2.285*0.1*pi) -> N >= 123 // The filter order is equal to the number of zero crossings, i.e. // it should be an even number (sinc is symmetric about x = 0). int N = (int)((A - 7.95) / (2.285 * dw) + 0.5); N += N & 1; double f_samples_per_cycle = sample_freq / clock_freq; double f_cycles_per_sample = clock_freq / sample_freq; // The filter length is equal to the filter order + 1. // The filter length must be an odd number (sinc is symmetric about x = // 0). fir_N = (int)(N * f_cycles_per_sample) + 1; fir_N |= 1; // We clamp the filter table resolution to 2^n, making the fixpoint // sample_offset a whole multiple of the filter table resolution. int res = method == SIDDefs.sampling_method.SAMPLE_RESAMPLE_INTERPOLATE ? FIR_RES_INTERPOLATE : FIR_RES_FAST; int n = (int)Math.Ceiling(Math.Log(res / f_cycles_per_sample) / Math.Log((double)2)); fir_RES = 1 << n; // Allocate memory for FIR tables. fir = null; fir = new short[fir_N * fir_RES]; // Calculate fir_RES FIR tables for linear interpolation. for (int i = 0; i < fir_RES; i++) { int fir_offset = i * fir_N + fir_N / 2; double j_offset = (double)(i) / fir_RES; // Calculate FIR table. This is the sinc function, weighted by the // Kaiser window. for (int j = -fir_N / 2; j <= fir_N / 2; j++) { double jx = j - j_offset; double wt = wc * jx / f_cycles_per_sample; double temp = jx / ((double)fir_N / 2d); double Kaiser = Math.Abs(temp) <= 1 ? I0(beta * Math.Sqrt(1 - temp * temp)) / I0beta : 0; double sincwt = Math.Abs(wt) >= 1e-6 ? Math.Sin(wt) / wt : 1; double val = (1 << FIR_SHIFT) * filter_scale * f_samples_per_cycle * wc / pi * sincwt * Kaiser; fir[fir_offset + j] = (short)(val + 0.5); } } // Allocate sample buffer. if ((sample == null)) { sample = new short[RINGSIZE * 2]; } // Clear sample buffer. for (int j = 0; j < RINGSIZE * 2; j++) { sample[j] = 0; } sample_index = 0; return(true); }