#include #include "printf/printf.h" #include "aica/aica.hpp" #include "interpreter.hpp" #include "sound.hpp" namespace interpreter { struct interpreter_state state = {}; // quater-semitones // // for i in range(48): // round(1024 * (2 ** (i / 48) - 1)) // const static int16_t cent_to_fns[] = { 0, 15, 30, 45, 61, 77, 93, 109, 125, 142, 159, 176, 194, 211, 229, 248, 266, 285, 304, 323, 343, 363, 383, 403, 424, 445, 467, 488, 510, 533, 555, 578, 601, 625, 649, 673, 698, 723, 749, 774, 801, 827, 854, 881, 909, 937, 966, 995 }; const int cent_to_fns_length = (sizeof (cent_to_fns)) / (sizeof (cent_to_fns[0])); uint16_t note_to_oct_fns(const int8_t note) { // log(8363 / 44100) / log(2) const float base_ratio = -2.3986861877015477; float c4_note = (float)note - 49.0; float ratio = base_ratio + (c4_note / 12.0); float whole = (int)ratio; float fraction; if (ratio < 0) { if (whole > ratio) whole -= 1; fraction = -(whole - ratio); } else { fraction = ratio - whole; } assert(fraction >= 0.0); assert(fraction < 1.0); int fns = cent_to_fns[(int)(fraction * cent_to_fns_length)]; return aica::oct_fns::OCT((int)whole) | aica::oct_fns::FNS((int)fns); } const static int8_t volume_table[] = { 0, 3, 5, 6, 7, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }; void _play_note(int ch, const xm_pattern_format_t * pf) { int instrument = (pf->instrument != 0) ? pf->instrument : state.channel[ch].instrument; if (instrument == 0) instrument = 1; state.channel[ch].instrument = instrument; xm_sample_header_t * sample_header = state.xm.sample_header[instrument - 1]; int start = state.xm.sample_data_offset[instrument - 1]; int sample_type = ((sample_header->type & (1 << 4)) != 0); int bytes_per_sample = 1 + sample_type; int loop_type = sample_header->type & 0b11; int lpctl = (loop_type == 0) ? 0 : 1; int lsa = s32(&sample_header->sample_loop_start) / bytes_per_sample; int len = s32(&sample_header->sample_loop_length) / bytes_per_sample; if (len == 0) { len = s32(&sample_header->sample_length) / bytes_per_sample; } if (len >= 65535) { len = 65532; } assert(start >= 0); assert(lsa >= 0); assert(len >= 0); if (loop_type == 2) // bidirectional len += len - 2; assert(sample_header->volume >= 0 && sample_header->volume <= 64); int disdl = volume_table[sample_header->volume]; bool pcms = !sample_type; wait(); aica_sound.channel[ch].PCMS(pcms); wait(); aica_sound.channel[ch].SA(start); wait(); aica_sound.channel[ch].LPCTL(lpctl); wait(); aica_sound.channel[ch].LSA((lsa) & ~(0b11)); wait(); aica_sound.channel[ch].LEA((lsa + len) & ~(0b11)); wait(); aica_sound.channel[ch].DISDL(disdl); wait(); aica_sound.channel[ch].oct_fns = note_to_oct_fns(pf->note + sample_header->relative_note_number); if (pf->effect_type == 0x04) { // vibrato wait(); aica_sound.channel[ch].LFOF(0x12); wait(); aica_sound.channel[ch].ALFOWS(2); wait(); aica_sound.channel[ch].PLFOWS(2); wait(); aica_sound.channel[ch].ALFOS(0); wait(); aica_sound.channel[ch].PLFOS(4); } else { //wait(); aica_sound.channel[ch].LFOF(0x11); //wait(); aica_sound.channel[ch].ALFOWS(2); //wait(); aica_sound.channel[ch].PLFOWS(2); wait(); aica_sound.channel[ch].ALFOS(0); wait(); aica_sound.channel[ch].PLFOS(0); } wait(); aica_sound.channel[ch].KYONB(1); } void play_note_effect(int ch, const xm_pattern_format_t * pf) { int effect_tick = (state.tick / 2) % state.ticks_per_line; switch (pf->effect_type) { case 0x04: // 4 vibrato wait(); aica_sound.channel[ch].LFOF(0x12); wait(); aica_sound.channel[ch].ALFOWS(2); wait(); aica_sound.channel[ch].PLFOWS(2); wait(); aica_sound.channel[ch].ALFOS(0); wait(); aica_sound.channel[ch].PLFOS(4); break; case 0x0d: // D pattern break state.pattern_break = pf->effect_parameter; break; case 0x0e: // E switch (pf->effect_parameter & 0xf0) { case 0xd0: // ED note delay if (effect_tick == (pf->effect_parameter & 0x0f)) { _play_note(ch, pf); } break; } break; case 0x14: // K delayed tick if (effect_tick == pf->effect_parameter) { wait(); aica_sound.channel[ch].KYONB(0); } break; } } void play_note(int ch, const xm_pattern_format_t * pf) { if (pf->note == 97) { wait(); aica_sound.channel[ch].KYONB(0); } else if (pf->note != 0) { bool note_delay = (pf->effect_type == 0xe) && ((pf->effect_parameter & 0xf0) == 0xd0); // ED note delay if (!note_delay) _play_note(ch, pf); } play_note_effect(ch, pf); } /* void play_debug_note(int ch, xm_pattern_format_t * pf) { debug_note(ch, pf); play_note(ch, pf); } */ void rekey_note(int ch, const xm_pattern_format_t * pf) { if (pf->note == 97) { } else if (pf->note != 0) { wait(); aica_sound.channel[ch].KYONB(0); } } static inline void next_pattern() { if (state.pattern_break >= 0) state.next_line_index = state.pattern_break; else state.next_line_index = 0; state.pattern_break = -1; state.pattern_order_table_index += 1; if (state.pattern_order_table_index >= state.xm.song_length) state.pattern_order_table_index = 0; printf("pattern_order_table_index: %d\n", state.pattern_order_table_index); state.pattern_index = state.xm.header->pattern_order_table[state.pattern_order_table_index]; printf("note_count: %d\n", state.xm.pattern_note_count[state.pattern_index]); } template void execute_line(int line_index) { int line_pattern_index = line_index * state.xm.number_of_channels; for (int ch = 0; ch < state.xm.number_of_channels; ch++) { xm_pattern_format_t * pattern = state.xm.pattern[state.pattern_index]; F(ch, &pattern[line_pattern_index + ch]); } } void interrupt() { bool keyoff_tick = (state.tick + 1) % (state.ticks_per_line * 2) == 0; bool note_tick = state.tick % (state.ticks_per_line * 2) == 0; bool effect_tick = (state.tick & 1) == 0; bool pattern_break_tick = (state.tick % (state.ticks_per_line * 2)) == (state.ticks_per_line * 2 - 1); if (keyoff_tick) { // execute keyoffs execute_line(state.next_line_index); } if (state.pattern_break >= 0 && pattern_break_tick) { printf("pattern_break\n"); next_pattern(); } if (note_tick) { // execute notes state.line_index = state.next_line_index; state.next_line_index += 1; execute_line(state.line_index); } else if (effect_tick) { // execute effects execute_line(state.line_index); } wait(); aica_sound.channel[0].KYONEX(1); int note_index = state.next_line_index * state.xm.number_of_channels; bool end_of_pattern = note_index >= state.xm.pattern_note_count[state.pattern_index]; if (end_of_pattern && pattern_break_tick) { printf("end_of_pattern\n"); next_pattern(); } state.tick += 1; } void init() { // 195 = 1ms // 2500 / bpm milliseconds int default_bpm = s16(&state.xm.header->default_bpm); int default_tempo = s16(&state.xm.header->default_tempo); int tick_rate = 195.32 * 2500 / default_bpm; printf("default_bpm %d\n", default_bpm); printf("default_tempo %d\n", default_tempo); printf("tick_rate %d\n", tick_rate); state.tick_rate = tick_rate; state.ticks_per_line = default_tempo; state.tick = 0; state.line_index = 0; state.pattern_order_table_index = -1; next_pattern(); printf("tick_rate %d\n", state.tick_rate); } }