Mercurial > repos > blastem
comparison ym2612.c @ 965:5257e85364ed
Implemented linear resampling and low pass filter for the YM2612
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 20 Apr 2016 09:18:58 -0700 |
parents | f1a8124ad881 |
children | 8d032a368dd5 |
comparison
equal
deleted
inserted
replaced
964:e6dc30231b83 | 965:5257e85364ed |
---|---|
114 wave_finalize(log_context->channels[i].logfile); | 114 wave_finalize(log_context->channels[i].logfile); |
115 } | 115 } |
116 } | 116 } |
117 log_context = NULL; | 117 log_context = NULL; |
118 } | 118 } |
119 #define BUFFER_INC_RES 1000000000UL | 119 #define BUFFER_INC_RES 0x40000000UL |
120 | 120 |
121 void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock) | 121 void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock) |
122 { | 122 { |
123 uint64_t old_inc = context->buffer_inc; | 123 uint64_t old_inc = context->buffer_inc; |
124 context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc; | 124 context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc * NUM_OPERATORS; |
125 } | 125 } |
126 | 126 |
127 #ifdef __ANDROID__ | 127 #ifdef __ANDROID__ |
128 #define log2(x) (log(x)/log(2)) | 128 #define log2(x) (log(x)/log(2)) |
129 #endif | 129 #endif |
130 | |
131 #define LOWPASS_CUTOFF 3390 | |
130 | 132 |
131 void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options) | 133 void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options) |
132 { | 134 { |
133 static uint8_t registered_finalize; | 135 static uint8_t registered_finalize; |
134 dfopen(debug_file, "ym_debug.txt", "w"); | 136 dfopen(debug_file, "ym_debug.txt", "w"); |
136 context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); | 138 context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); |
137 context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); | 139 context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); |
138 context->sample_rate = sample_rate; | 140 context->sample_rate = sample_rate; |
139 context->clock_inc = clock_div * 6; | 141 context->clock_inc = clock_div * 6; |
140 ym_adjust_master_clock(context, master_clock); | 142 ym_adjust_master_clock(context, master_clock); |
143 | |
144 double rc = (1.0 / (double)LOWPASS_CUTOFF) / (2.0 * M_PI); | |
145 double dt = 1.0 / ((double)master_clock / (double)(context->clock_inc * NUM_OPERATORS)); | |
146 double alpha = dt / (dt + rc); | |
147 context->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | |
141 | 148 |
142 context->sample_limit = sample_limit*2; | 149 context->sample_limit = sample_limit*2; |
143 context->write_cycle = CYCLE_NEVER; | 150 context->write_cycle = CYCLE_NEVER; |
144 for (int i = 0; i < NUM_OPERATORS; i++) { | 151 for (int i = 0; i < NUM_OPERATORS; i++) { |
145 context->operators[i].envelope = MAX_ENVELOPE; | 152 context->operators[i].envelope = MAX_ENVELOPE; |
476 } | 483 } |
477 } | 484 } |
478 //puts("operator update done"); | 485 //puts("operator update done"); |
479 } | 486 } |
480 context->current_op++; | 487 context->current_op++; |
481 context->buffer_fraction += context->buffer_inc; | |
482 if (context->current_op == NUM_OPERATORS) { | 488 if (context->current_op == NUM_OPERATORS) { |
483 context->current_op = 0; | 489 context->current_op = 0; |
484 } | 490 |
485 if (context->buffer_fraction > BUFFER_INC_RES) { | 491 context->buffer_fraction += context->buffer_inc; |
486 context->buffer_fraction -= BUFFER_INC_RES; | 492 int16_t left = 0, right = 0; |
487 context->audio_buffer[context->buffer_pos] = 0; | |
488 context->audio_buffer[context->buffer_pos + 1] = 0; | |
489 for (int i = 0; i < NUM_CHANNELS; i++) { | 493 for (int i = 0; i < NUM_CHANNELS; i++) { |
490 int16_t value = context->channels[i].output; | 494 int16_t value = context->channels[i].output; |
491 if (value > 0x1FE0) { | 495 if (value > 0x1FE0) { |
492 value = 0x1FE0; | 496 value = 0x1FE0; |
493 } else if (value < -0x1FF0) { | 497 } else if (value < -0x1FF0) { |
496 value &= 0x3FE0; | 500 value &= 0x3FE0; |
497 if (value & 0x2000) { | 501 if (value & 0x2000) { |
498 value |= 0xC000; | 502 value |= 0xC000; |
499 } | 503 } |
500 } | 504 } |
501 if (context->channels[i].logfile) { | 505 if (context->channels[i].logfile && context->buffer_fraction > BUFFER_INC_RES) { |
502 fwrite(&value, sizeof(value), 1, context->channels[i].logfile); | 506 fwrite(&value, sizeof(value), 1, context->channels[i].logfile); |
503 } | 507 } |
504 if (context->channels[i].lr & 0x80) { | 508 if (context->channels[i].lr & 0x80) { |
505 context->audio_buffer[context->buffer_pos] += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER; | 509 left += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER; |
506 } | 510 } |
507 if (context->channels[i].lr & 0x40) { | 511 if (context->channels[i].lr & 0x40) { |
508 context->audio_buffer[context->buffer_pos+1] += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER; | 512 right += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER; |
509 } | 513 } |
510 } | 514 } |
511 context->buffer_pos += 2; | 515 int32_t tmp = left * context->lowpass_alpha + context->last_left * (0x10000 - context->lowpass_alpha); |
512 if (context->buffer_pos == context->sample_limit) { | 516 left = tmp >> 16; |
513 if (!headless) { | 517 tmp = right * context->lowpass_alpha + context->last_right * (0x10000 - context->lowpass_alpha); |
514 render_wait_ym(context); | 518 right = tmp >> 16; |
515 } | 519 if (context->buffer_fraction > BUFFER_INC_RES) { |
516 } | 520 context->buffer_fraction -= BUFFER_INC_RES; |
517 } | 521 |
522 int64_t tmp = context->last_left * ((context->buffer_fraction << 16) / context->buffer_inc); | |
523 tmp += left * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc)); | |
524 context->audio_buffer[context->buffer_pos] = tmp >> 16; | |
525 | |
526 tmp = context->last_right * ((context->buffer_fraction << 16) / context->buffer_inc); | |
527 tmp += right * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc)); | |
528 context->audio_buffer[context->buffer_pos+1] = tmp >> 16; | |
529 | |
530 context->buffer_pos += 2; | |
531 if (context->buffer_pos == context->sample_limit) { | |
532 if (!headless) { | |
533 render_wait_ym(context); | |
534 } | |
535 } | |
536 } | |
537 context->last_left = left; | |
538 context->last_right = right; | |
539 } | |
540 | |
518 } | 541 } |
519 if (context->current_cycle >= context->write_cycle + (context->busy_cycles * context->clock_inc / 6)) { | 542 if (context->current_cycle >= context->write_cycle + (context->busy_cycles * context->clock_inc / 6)) { |
520 context->status &= 0x7F; | 543 context->status &= 0x7F; |
521 context->write_cycle = CYCLE_NEVER; | 544 context->write_cycle = CYCLE_NEVER; |
522 } | 545 } |