Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 716 | lvd | 1 | #include "../std.h" |
| 2 | |||
| 3 | #include "../emul.h" |
||
| 4 | #include "../vars.h" |
||
| 5 | |||
| 6 | /* |
||
| 7 | sound resampling core for Unreal Speccy project |
||
| 8 | created under public domain license by SMT, jan.2006 |
||
| 9 | */ |
||
| 10 | |||
| 11 | #include "sndrender.h" |
||
| 12 | |||
| 13 | unsigned SNDRENDER::render(SNDOUT *src, unsigned srclen, unsigned clk_ticks, bufptr_t dst_start) |
||
| 14 | { |
||
| 15 | start_frame(dst_start); |
||
| 16 | for (unsigned index = 0; index < srclen; index++) |
||
| 17 | { |
||
| 18 | // if (src[index].timestamp > clk_ticks) continue; // wrong input data leads to crash |
||
| 19 | update(src[index].timestamp, src[index].newvalue.ch.left, src[index].newvalue.ch.right); |
||
| 20 | } |
||
| 21 | return end_frame(clk_ticks); |
||
| 22 | } |
||
| 23 | |||
| 24 | const unsigned TICK_F = (1<<TICK_FF); |
||
| 25 | |||
| 26 | // b = 1+ln2(max_sndtick) = 1+ln2((max_sndfq*TICK_F)/min_intfq) = 1+ln2(48000*64/10) ~= 19.2; |
||
| 27 | // assert(b+MULT_C <= 32) |
||
| 28 | |||
| 29 | |||
| 30 | void SNDRENDER::start_frame(bufptr_t dst_start) |
||
| 31 | { |
||
| 32 | SNDRENDER::dst_start = dstpos = dst_start; |
||
| 33 | base_tick = tick; |
||
| 34 | firstsmp = 4; //Alone Coder |
||
| 35 | } |
||
| 36 | |||
| 37 | void SNDRENDER::update(unsigned timestamp, unsigned l, unsigned r) |
||
| 38 | { |
||
| 39 | if (!((l ^ mix_l) | (r ^ mix_r))) // (l == mix_l) && (r == mix_r) |
||
| 40 | return; |
||
| 41 | |||
| 42 | //[vv] unsigned endtick = (timestamp * mult_const) >> MULT_C; |
||
| 43 | uint64_t endtick = (timestamp * (uint64_t)sample_rate * TICK_F) / clock_rate; |
||
| 44 | flush( (unsigned) (base_tick + endtick) ); |
||
| 45 | mix_l = l; mix_r = r; |
||
| 46 | } |
||
| 47 | |||
| 48 | unsigned SNDRENDER::end_frame(unsigned clk_ticks) |
||
| 49 | { |
||
| 50 | // adjusting 'clk_ticks' with whole history will fix accumulation of rounding errors |
||
| 51 | //uint64_t endtick = ((passed_clk_ticks + clk_ticks) * mult_const) >> MULT_C; |
||
| 52 | uint64_t endtick = ((passed_clk_ticks + clk_ticks) * (uint64_t)sample_rate * TICK_F) / clock_rate; |
||
| 53 | flush( (unsigned) (endtick - passed_snd_ticks) ); |
||
| 54 | |||
| 55 | unsigned ready_samples = dstpos - dst_start; |
||
| 56 | #ifdef SND_EXTERNAL_BUFFER |
||
| 57 | if ((int)ready_samples < 0) ready_samples += SND_EXTERNAL_BUFFER_SIZE; |
||
| 58 | #endif |
||
| 59 | |||
| 60 | oldfrmleft = ((long)useleft + (long)olduseleft)/2; //Alone Coder |
||
| 61 | oldfrmright = ((long)useright + (long)olduseright)/2; //Alone Coder |
||
| 62 | |||
| 63 | tick -= (ready_samples << TICK_FF); |
||
| 64 | passed_snd_ticks += (ready_samples << TICK_FF); |
||
| 65 | passed_clk_ticks += clk_ticks; |
||
| 66 | |||
| 67 | return ready_samples; |
||
| 68 | } |
||
| 69 | |||
| 70 | /* |
||
| 71 | unsigned SNDRENDER::end_empty_frame(unsigned clk_ticks) |
||
| 72 | { |
||
| 73 | // adjusting 'clk_ticks' with whole history will fix accumulation of rounding errors |
||
| 74 | //uint64_t endtick = ((passed_clk_ticks + clk_ticks) * mult_const) >> MULT_C; |
||
| 75 | uint64_t endtick = ((passed_clk_ticks + clk_ticks) * (uint64_t)sample_rate * TICK_F) / clock_rate; |
||
| 76 | tick = (unsigned)(endtick-passed_snd_ticks); // flush(endtick-passed_snd_ticks); |
||
| 77 | // todo: change dstpos! |
||
| 78 | |||
| 79 | unsigned ready_samples = dstpos - dst_start; |
||
| 80 | #ifdef SND_EXTERNAL_BUFFER |
||
| 81 | if ((int)ready_samples < 0) ready_samples += SND_EXTERNAL_BUFFER_SIZE; |
||
| 82 | #endif |
||
| 83 | |||
| 84 | tick -= (ready_samples << TICK_FF); |
||
| 85 | passed_snd_ticks += (ready_samples << TICK_FF); |
||
| 86 | passed_clk_ticks += clk_ticks; |
||
| 87 | |||
| 88 | return ready_samples; |
||
| 89 | } |
||
| 90 | */ |
||
| 91 | |||
| 92 | void SNDRENDER::set_timings(unsigned clock_rate, unsigned sample_rate) |
||
| 93 | { |
||
| 94 | SNDRENDER::clock_rate = clock_rate; |
||
| 95 | SNDRENDER::sample_rate = sample_rate; |
||
| 96 | |||
| 97 | tick = 0; dstpos = dst_start = 0; |
||
| 98 | passed_snd_ticks = passed_clk_ticks = 0; |
||
| 99 | |||
| 784 | DimkaM | 100 | // mult_const = (unsigned) (((uint64_t)sample_rate << (MULT_C+TICK_FF)) / clock_rate); |
| 716 | lvd | 101 | } |
| 102 | |||
| 103 | |||
| 784 | DimkaM | 104 | static unsigned filter_diff[TICK_F*2]; // discrete-time step response |
| 716 | lvd | 105 | const double filter_sum_full = 1.0, filter_sum_half = 0.5; |
| 106 | const unsigned filter_sum_full_u = (unsigned)(filter_sum_full * 0x10000), |
||
| 107 | filter_sum_half_u = (unsigned)(filter_sum_half * 0x10000); |
||
| 108 | |||
| 109 | void SNDRENDER::flush(unsigned endtick) |
||
| 110 | { |
||
| 784 | DimkaM | 111 | unsigned scale; |
| 112 | if(!((endtick ^ tick) & ~(TICK_F - 1))) // (endtick / TICK_F) == (tick / TICK_F) |
||
| 113 | { // Оптимизация, децимация не нужна, т.к. после децимации tick совпадет с endtick |
||
| 114 | // Входной сигнал изменился быстрее чем один период Fs выходного сигнала |
||
| 115 | // s1 - значение выходного сигнала в левом узле децимации |
||
| 116 | // s2 - значение выходного сигнала в правом узле децимации |
||
| 117 | // mix - значение входного сигнала в интервале [tick.. endtick) |
||
| 716 | lvd | 118 | |
| 784 | DimkaM | 119 | //same discrete as before |
| 120 | // Правая точка вычисленная по переходной характеристике |
||
| 121 | scale = filter_diff[(endtick & (TICK_F - 1)) + TICK_F] - filter_diff[(tick & (TICK_F - 1)) + TICK_F]; |
||
| 122 | s2_l += mix_l * scale; |
||
| 123 | s2_r += mix_r * scale; |
||
| 716 | lvd | 124 | |
| 784 | DimkaM | 125 | // Левая точка вычисленная по переходной характеристике |
| 126 | scale = filter_diff[endtick & (TICK_F - 1)] - filter_diff[tick & (TICK_F - 1)]; |
||
| 127 | s1_l += mix_l * scale; |
||
| 128 | s1_r += mix_r * scale; |
||
| 716 | lvd | 129 | |
| 784 | DimkaM | 130 | tick = endtick; |
| 131 | return; |
||
| 132 | } |
||
| 716 | lvd | 133 | |
| 784 | DimkaM | 134 | // Отрезок [TICK_F + (tick % TICK_F) ... 2*TICK_F-1] |
| 135 | scale = filter_sum_full_u - filter_diff[(tick & (TICK_F - 1)) + TICK_F]; // filter_sum_full_u = filter_diff[(TICK_F - 1) + TICK_F] |
||
| 716 | lvd | 136 | |
| 784 | DimkaM | 137 | unsigned sample_value; |
| 138 | if(conf.soundfilter) |
||
| 139 | { |
||
| 140 | /*lame noise reduction by Alone Coder*/ |
||
| 141 | int templeft = int(mix_l * scale + s2_l); |
||
| 142 | /*olduseleft = useleft; |
||
| 143 | if (firstsmp)useleft=oldfrmleft,firstsmp--; |
||
| 144 | else*/ useleft = ((long)templeft + (long)oldleft) / 2; |
||
| 145 | oldleft = templeft; |
||
| 146 | int tempright = int(mix_r * scale + s2_r); |
||
| 147 | /*olduseright = useright; |
||
| 148 | if (firstsmp)useright=oldfrmright,firstsmp--; |
||
| 149 | else*/ useright = ((long)tempright + (long)oldright) / 2; |
||
| 150 | oldright = tempright; |
||
| 151 | sample_value = unsigned((useleft >> 16) + int(unsigned(useright) & 0xFFFF0000)); |
||
| 152 | /**/ |
||
| 153 | } |
||
| 154 | else |
||
| 155 | { |
||
| 156 | sample_value = ((mix_l*scale + s2_l) >> 16) + |
||
| 157 | ((mix_r*scale + s2_r) & 0xFFFF0000); |
||
| 158 | } |
||
| 716 | lvd | 159 | |
| 784 | DimkaM | 160 | #ifdef SND_EXTERNAL_BUFFER |
| 161 | SND_EXTERNAL_BUFFER[dstpos].sample += sample_value; |
||
| 162 | dstpos = (dstpos + 1) & (SND_EXTERNAL_BUFFER_SIZE - 1); |
||
| 163 | #else |
||
| 164 | dstpos->sample = sample_value; |
||
| 165 | dstpos++; |
||
| 166 | #endif |
||
| 716 | lvd | 167 | |
| 784 | DimkaM | 168 | // Отрезок [tick % TICK_F ... TICK_F-1] |
| 169 | scale = filter_sum_half_u - filter_diff[tick & (TICK_F - 1)];// filter_sum_half_u = filter_diff[TICK_F - 1] |
||
| 170 | s2_l = s1_l + mix_l * scale; |
||
| 171 | s2_r = s1_r + mix_r * scale; |
||
| 172 | |||
| 173 | // Выравнивание до узла децимации |
||
| 174 | tick = (tick | (TICK_F - 1)) + 1; |
||
| 175 | |||
| 176 | // Цикл децимации в TICK_F раз, tick уже выровнен до сетки с шагом TICK_F |
||
| 177 | // endtick / TICK_F - endtick после децимации в TICK_F раз |
||
| 178 | // tick / TICK_F - tick после децимации в TICK_F раз |
||
| 179 | if((endtick ^ tick) & ~(TICK_F - 1)) // (endtick / TICK_F) != (tick / TICK_F) |
||
| 180 | { // Оптимизация, проверка на несовпадение tick и endtick после децимации |
||
| 181 | |||
| 182 | // assume filter_coeff is symmetric |
||
| 183 | // Отрезок [0 ... TICK_F-1] |
||
| 184 | unsigned val_l = mix_l * filter_sum_half_u; |
||
| 185 | unsigned val_r = mix_r * filter_sum_half_u; |
||
| 186 | |||
| 187 | // Цикл децимации, [tick/TICK_F ... endtick/TICK_F) |
||
| 188 | do |
||
| 189 | { |
||
| 716 | lvd | 190 | if(conf.soundfilter) |
| 191 | { |
||
| 192 | /*lame noise reduction by Alone Coder*/ |
||
| 784 | DimkaM | 193 | int templeft = int(s2_l + val_l); |
| 716 | lvd | 194 | /*olduseleft = useleft; |
| 195 | if (firstsmp)useleft=oldfrmleft,firstsmp--; |
||
| 784 | DimkaM | 196 | else*/ useleft = ((long)templeft + (long)oldleft) / 2; |
| 716 | lvd | 197 | oldleft = templeft; |
| 784 | DimkaM | 198 | int tempright = int(s2_r + val_r); |
| 716 | lvd | 199 | /*olduseright = useright; |
| 200 | if (firstsmp)useright=oldfrmright,firstsmp--; |
||
| 784 | DimkaM | 201 | else*/ useright = ((long)tempright + (long)oldright) / 2; |
| 716 | lvd | 202 | oldright = tempright; |
| 784 | DimkaM | 203 | sample_value = unsigned((useleft >> 16) + int(unsigned(useright) & 0xFFFF0000)); |
| 716 | lvd | 204 | /**/ |
| 205 | } |
||
| 206 | else |
||
| 207 | { |
||
| 208 | sample_value = ((s2_l + val_l) >> 16) + |
||
| 784 | DimkaM | 209 | ((s2_r + val_r) & 0xFFFF0000); // save s2+val |
| 716 | lvd | 210 | } |
| 211 | |||
| 784 | DimkaM | 212 | #ifdef SND_EXTERNAL_BUFFER |
| 716 | lvd | 213 | SND_EXTERNAL_BUFFER[dstpos].sample += sample_value; |
| 784 | DimkaM | 214 | dstpos = (dstpos + 1) & (SND_EXTERNAL_BUFFER_SIZE - 1); |
| 215 | #else |
||
| 716 | lvd | 216 | dstpos->sample = sample_value; |
| 217 | dstpos++; |
||
| 784 | DimkaM | 218 | #endif |
| 716 | lvd | 219 | |
| 220 | tick += TICK_F; |
||
| 784 | DimkaM | 221 | s2_l = val_l; |
| 222 | s2_r = val_r; // s2=s1, s1=0; |
||
| 716 | lvd | 223 | |
| 784 | DimkaM | 224 | } while((endtick ^ tick) & ~(TICK_F - 1)); // (endtick / TICK_F) != (tick / TICK_F) |
| 225 | } |
||
| 716 | lvd | 226 | |
| 784 | DimkaM | 227 | tick = endtick; |
| 716 | lvd | 228 | |
| 784 | DimkaM | 229 | scale = filter_diff[(endtick & (TICK_F - 1)) + TICK_F] - filter_sum_half_u; // filter_sum_half_u = filter_diff[TICK_F - 1] |
| 230 | s2_l += mix_l * scale; |
||
| 231 | s2_r += mix_r * scale; |
||
| 716 | lvd | 232 | |
| 784 | DimkaM | 233 | scale = filter_diff[endtick & (TICK_F - 1)]; |
| 234 | s1_l = mix_l * scale; |
||
| 235 | s1_r = mix_r * scale; |
||
| 716 | lvd | 236 | } |
| 237 | |||
| 784 | DimkaM | 238 | // bw = 1*(10^-2)*pi rad/smp (-3dB) |
| 239 | // bw = 2*(10^-2)*pi rad/smp (-10dB) |
||
| 240 | // bw = 20 kHz (fs=44100 * 64 = 2822400) |
||
| 241 | // matlab: fvtool(filter_coeff) |
||
| 242 | |||
| 243 | // Параметры синтеза фильтра: |
||
| 244 | // matlab: (fdatool/filterDesigner) |
||
| 245 | // FIR: Window (Hamming) |
||
| 246 | // order: 127 |
||
| 247 | // Fs: 2822400 |
||
| 248 | // Fc: 11025 |
||
| 249 | |||
| 716 | lvd | 250 | const double filter_coeff[TICK_F*2] = |
| 251 | { |
||
| 252 | // filter designed with Matlab's DSP toolbox |
||
| 253 | 0.000797243121022152, 0.000815206499600866, 0.000844792477531490, 0.000886460636664257, |
||
| 254 | 0.000940630171246217, 0.001007677515787512, 0.001087934129054332, 0.001181684445143001, |
||
| 255 | 0.001289164001921830, 0.001410557756409498, 0.001545998595893740, 0.001695566052785407, |
||
| 256 | 0.001859285230354019, 0.002037125945605404, 0.002229002094643918, 0.002434771244914945, |
||
| 257 | 0.002654234457752337, 0.002887136343664226, 0.003133165351783907, 0.003391954293894633, |
||
| 258 | 0.003663081102412781, 0.003946069820687711, 0.004240391822953223, 0.004545467260249598, |
||
| 259 | 0.004860666727631453, 0.005185313146989532, 0.005518683858848785, 0.005860012915564928, |
||
| 260 | 0.006208493567431684, 0.006563280932335042, 0.006923494838753613, 0.007288222831108771, |
||
| 261 | 0.007656523325719262, 0.008027428904915214, 0.008399949736219575, 0.008773077102914008, |
||
| 262 | 0.009145787031773989, 0.009517044003286715, 0.009885804729257883, 0.010251021982371376, |
||
| 263 | 0.010611648461991030, 0.010966640680287394, 0.011314962852635887, 0.011655590776166550, |
||
| 264 | 0.011987515680350414, 0.012309748033583185, 0.012621321289873522, 0.012921295559959939, |
||
| 265 | 0.013208761191466523, 0.013482842243062109, 0.013742699838008606, 0.013987535382970279, |
||
| 266 | 0.014216593638504731, 0.014429165628265581, 0.014624591374614174, 0.014802262449059521, |
||
| 267 | 0.014961624326719471, 0.015102178534818147, 0.015223484586101132, 0.015325161688957322, |
||
| 268 | 0.015406890226980602, 0.015468413001680802, 0.015509536233058410, 0.015530130313785910, |
||
| 269 | 0.015530130313785910, 0.015509536233058410, 0.015468413001680802, 0.015406890226980602, |
||
| 270 | 0.015325161688957322, 0.015223484586101132, 0.015102178534818147, 0.014961624326719471, |
||
| 271 | 0.014802262449059521, 0.014624591374614174, 0.014429165628265581, 0.014216593638504731, |
||
| 272 | 0.013987535382970279, 0.013742699838008606, 0.013482842243062109, 0.013208761191466523, |
||
| 273 | 0.012921295559959939, 0.012621321289873522, 0.012309748033583185, 0.011987515680350414, |
||
| 274 | 0.011655590776166550, 0.011314962852635887, 0.010966640680287394, 0.010611648461991030, |
||
| 275 | 0.010251021982371376, 0.009885804729257883, 0.009517044003286715, 0.009145787031773989, |
||
| 276 | 0.008773077102914008, 0.008399949736219575, 0.008027428904915214, 0.007656523325719262, |
||
| 277 | 0.007288222831108771, 0.006923494838753613, 0.006563280932335042, 0.006208493567431684, |
||
| 278 | 0.005860012915564928, 0.005518683858848785, 0.005185313146989532, 0.004860666727631453, |
||
| 279 | 0.004545467260249598, 0.004240391822953223, 0.003946069820687711, 0.003663081102412781, |
||
| 280 | 0.003391954293894633, 0.003133165351783907, 0.002887136343664226, 0.002654234457752337, |
||
| 281 | 0.002434771244914945, 0.002229002094643918, 0.002037125945605404, 0.001859285230354019, |
||
| 282 | 0.001695566052785407, 0.001545998595893740, 0.001410557756409498, 0.001289164001921830, |
||
| 283 | 0.001181684445143001, 0.001087934129054332, 0.001007677515787512, 0.000940630171246217, |
||
| 284 | 0.000886460636664257, 0.000844792477531490, 0.000815206499600866, 0.000797243121022152 |
||
| 285 | }; |
||
| 286 | |||
| 784 | DimkaM | 287 | // h - impulse response |
| 288 | // s - step response |
||
| 289 | // h[n] = s[n] - s[n-1] |
||
| 290 | |||
| 291 | // y[n] = sum(x[k]*h[n-k]) = sum(h[k]*x[n-k]) |
||
| 292 | // k k |
||
| 293 | // Для кусочно постоянного сигнала применима оптимизация в вычислениях (нет необходимости вычислять промежуточные точки) |
||
| 294 | // y[n] = y[k] + x*(s[n] - s[k]) |
||
| 295 | // где n и k - концы интервала, на котором сигнал x постоянен |
||
| 296 | |||
| 297 | // Общие формулы: |
||
| 298 | // u - unit step |
||
| 299 | // s - step response |
||
| 300 | // inf |
||
| 301 | // x[n] = x[-inf] + sum((x[k]-x[k-1])u[n-k]) |
||
| 302 | // -inf |
||
| 303 | // |
||
| 304 | // inf |
||
| 305 | // y[n] = x[-inf]s[inf] + sum(x[k]-x[k-1])s[n-k] |
||
| 306 | // -inf |
||
| 307 | // Из формкл видно, что значения имеют только фронты сигнала, там где сигнал постоянный слагаеые обращаются в ноль |
||
| 308 | // отсюда становится возможна кусочно-постоянная оптимизация |
||
| 309 | |||
| 310 | // Для YM (значения берутся из конфига) |
||
| 311 | // для примера: |
||
| 312 | // clock_rate = 1750000 / 8 = 218750 |
||
| 313 | // 1774400 / 8 = 221800 |
||
| 314 | // sample_rate = 44100 |
||
| 315 | // ПЧ = sample_rate * 64 = 2822400 |
||
| 316 | // Перенос входного сигнала на ПЧ делается домножением на (sample_rate*64)/clock_rate = |
||
| 317 | // 12.9024 (для clock_rate = 1.7500) и 12.724977 (для clock_rate = 1.7744) |
||
| 318 | // На ПЧ делается фильтрация и децимация в 64 раза до sample_rate |
||
| 319 | |||
| 716 | lvd | 320 | SNDRENDER::SNDRENDER() |
| 321 | { |
||
| 322 | set_timings(SNDR_DEFAULT_SYSTICK_RATE, SNDR_DEFAULT_SAMPLE_RATE); |
||
| 323 | |||
| 324 | static char diff_ready = 0; |
||
| 325 | if (!diff_ready) { |
||
| 326 | double sum = 0; |
||
| 784 | DimkaM | 327 | for (unsigned i = 0; i < TICK_F*2; i++) { // calculate discrete-time step response |
| 328 | filter_diff[i] = unsigned(sum * 0x10000); |
||
| 329 | sum += filter_coeff[i]; // Тут ошибка, сумму надо вычислять до построения переходной характеристики, чтобы filter_diff[0] == filter_coeff[0] |
||
| 330 | // filter_diff[TICK_F - 1] == 0.5, filter_diff[2*TICK_F - 1] == 1.0 |
||
| 716 | lvd | 331 | } |
| 332 | diff_ready = 1; |
||
| 333 | } |
||
| 334 | } |