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 }