comparison ym2612.c @ 411:baf4688901f2

Initial stab at LFO phase modulation
author Mike Pavone <pavone@retrodev.com>
date Wed, 19 Jun 2013 00:25:09 -0700
parents c3abc4ada43d
children 7e8e179116af
comparison
equal deleted inserted replaced
410:41c079dbee73 411:baf4688901f2
17 17
18 #define BUSY_CYCLES 17 18 #define BUSY_CYCLES 17
19 #define OP_UPDATE_PERIOD 144 19 #define OP_UPDATE_PERIOD 144
20 20
21 enum { 21 enum {
22 REG_LFO = 0x22,
22 REG_TIMERA_HIGH = 0x24, 23 REG_TIMERA_HIGH = 0x24,
23 REG_TIMERA_LOW, 24 REG_TIMERA_LOW,
24 REG_TIMERB, 25 REG_TIMERB,
25 REG_TIME_CTRL, 26 REG_TIME_CTRL,
26 REG_KEY_ONOFF, 27 REG_KEY_ONOFF,
83 1,2,1,2,1,2,1,2, 84 1,2,1,2,1,2,1,2,
84 1,2,2,2,1,2,2,2, 85 1,2,2,2,1,2,2,2,
85 }; 86 };
86 87
87 uint16_t rate_table[64*8]; 88 uint16_t rate_table[64*8];
89
90 uint8_t lfo_timer_values[] = {108, 77, 71, 67, 62, 44, 8, 5};
91 uint8_t lfo_pm_base[][8] = {
92 {0, 0, 0, 0, 0, 0, 0, 0},
93 {0, 0, 0, 0, 4, 4, 4, 4},
94 {0, 0, 0, 4, 4, 4, 8, 8},
95 {0, 0, 4, 4, 8, 8, 0xc, 0xc},
96 {0, 0, 4, 8, 8, 8, 0xc,0x10},
97 {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
98 {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
99 {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}
100 };
101 int16_t lfo_pm_table[128 * 32 * 8];
88 102
89 #define MAX_ENVELOPE 0xFFC 103 #define MAX_ENVELOPE 0xFFC
90 #define YM_DIVIDER 2 104 #define YM_DIVIDER 2
91 #define CYCLE_NEVER 0xFFFFFFFF 105 #define CYCLE_NEVER 0xFFFFFFFF
92 106
180 value = rate_table_base[32 + (rate & 0x3) * 8 + cycle] << ((rate - 48) >> 2); 194 value = rate_table_base[32 + (rate & 0x3) * 8 + cycle] << ((rate - 48) >> 2);
181 } 195 }
182 rate_table[rate * 8 + cycle] = value; 196 rate_table[rate * 8 + cycle] = value;
183 } 197 }
184 } 198 }
199 //populate LFO PM table from small base table
200 //seems like there must be a better way to derive this
201 for (int freq = 0; freq < 128; freq++) {
202 for (int pms = 0; pms < 8; pms++) {
203 for (int step = 0; step < 32; step++) {
204 int16_t value = 0;
205 for (int bit = 0x40, shift = 0; bit > 0; bit >>= 1, shift++) {
206 if (freq & bit) {
207 value += lfo_pm_base[pms][(step & 0x8) ? 7-step & 7 : step & 7] >> shift;
208 }
209 }
210 if (step & 0x10) {
211 value = -value;
212 }
213 lfo_pm_table[freq * 256 + pms * 32 + step] = value;
214 }
215 }
216 }
185 } 217 }
186 } 218 }
187 219
188 #define YM_VOLUME_DIVIDER 2 220 #define YM_VOLUME_DIVIDER 2
189 #define YM_MOD_SHIFT 1 221 #define YM_MOD_SHIFT 1
215 if (context->timer_control & BIT_TIMERB_OVEREN) { 247 if (context->timer_control & BIT_TIMERB_OVEREN) {
216 context->status |= BIT_STATUS_TIMERB; 248 context->status |= BIT_STATUS_TIMERB;
217 } 249 }
218 context->timer_b = context->timer_b_load; 250 context->timer_b = context->timer_b_load;
219 } 251 }
252 }
253 }
254 //Update LFO
255 if (context->lfo_enable) {
256 if (context->lfo_counter) {
257 context->lfo_counter--;
258 } else {
259 context->lfo_counter = lfo_timer_values[context->lfo_freq];
260 context->lfo_am_step += 2;
261 context->lfo_am_step &= 0xFE;
262 context->lfo_pm_step = context->lfo_am_step / 8;
220 } 263 }
221 } 264 }
222 //Update Envelope Generator 265 //Update Envelope Generator
223 if (!(context->current_op % 3)) { 266 if (!(context->current_op % 3)) {
224 uint32_t env_cyc = context->env_counter; 267 uint32_t env_cyc = context->env_counter;
294 ym_operator * operator = context->operators + op; 337 ym_operator * operator = context->operators + op;
295 ym_channel * chan = context->channels + channel; 338 ym_channel * chan = context->channels + channel;
296 //TODO: Modulate phase by LFO if necessary 339 //TODO: Modulate phase by LFO if necessary
297 uint16_t phase = operator->phase_counter >> 10 & 0x3FF; 340 uint16_t phase = operator->phase_counter >> 10 & 0x3FF;
298 operator->phase_counter += operator->phase_inc; 341 operator->phase_counter += operator->phase_inc;
342 if (chan->pms) {
343 //not entirely sure this will get the precision correct, but I'd like to avoid recalculating phase
344 //increment every update when LFO phase modulation is enabled
345 int16_t lfo_mod = lfo_pm_table[(chan->fnum & 0x7F0) * 16 + chan->pms + context->lfo_pm_step];
346 if (operator->multiple) {
347 lfo_mod *= operator->multiple;
348 } else {
349 lfo_mod >>= 1;
350 }
351 operator->phase_counter += lfo_mod;
352 }
299 int16_t mod = 0; 353 int16_t mod = 0;
300 switch (op % 4) 354 switch (op % 4)
301 { 355 {
302 case 0://Operator 1 356 case 0://Operator 1
303 if (chan->feedback) { 357 if (chan->feedback) {
534 dfprintf(debug_file, "write of %X to reg %X in part %d\n", value, context->selected_reg, context->selected_part+1); 588 dfprintf(debug_file, "write of %X to reg %X in part %d\n", value, context->selected_reg, context->selected_part+1);
535 if (context->selected_reg < 0x30) { 589 if (context->selected_reg < 0x30) {
536 //Shared regs 590 //Shared regs
537 switch (context->selected_reg) 591 switch (context->selected_reg)
538 { 592 {
539 //TODO: Test reg and LFO 593 //TODO: Test reg
594 case REG_LFO:
595 if ((value & 0x8) && !context->lfo_enable) {
596 printf("LFO Enabled, Freq: %d\n", value & 0x7);
597 }
598 context->lfo_enable = value & 0x8;
599 if (!context->lfo_enable) {
600 context->lfo_am_step = context->lfo_pm_step = 0;
601 }
602 context->lfo_freq = value & 0x7;
603
604 break;
540 case REG_TIMERA_HIGH: 605 case REG_TIMERA_HIGH:
541 context->timer_a_load &= 0x3; 606 context->timer_a_load &= 0x3;
542 context->timer_a_load |= value << 2; 607 context->timer_a_load |= value << 2;
543 break; 608 break;
544 case REG_TIMERA_LOW: 609 case REG_TIMERA_LOW:
678 case REG_ALG_FEEDBACK: 743 case REG_ALG_FEEDBACK:
679 context->channels[channel].algorithm = value & 0x7; 744 context->channels[channel].algorithm = value & 0x7;
680 context->channels[channel].feedback = value >> 3 & 0x7; 745 context->channels[channel].feedback = value >> 3 & 0x7;
681 break; 746 break;
682 case REG_LR_AMS_PMS: 747 case REG_LR_AMS_PMS:
683 context->channels[channel].pms = value & 0x7; 748 context->channels[channel].pms = (value & 0x7) * 32;
684 context->channels[channel].ams = value >> 4 & 0x3; 749 context->channels[channel].ams = value >> 4 & 0x3;
685 context->channels[channel].lr = value & 0xC0; 750 context->channels[channel].lr = value & 0xC0;
686 //printf("Write of %X to LR_AMS_PMS reg for channel %d\n", value, channel); 751 //printf("Write of %X to LR_AMS_PMS reg for channel %d\n", value, channel);
687 break; 752 break;
688 } 753 }