Mercurial > repos > blastem
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 } |