tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE
commit 5569729cba729800db5460d25c74c8cbedd1e3c1
parent 3b617a94f29bf2bb6bd66598363ffd3aeb08f890
Author: Connor Thomson <connor@gnufault.org>
Date:   Wed,  4 Nov 2026 21:45:56 -0800

Add horrible audio subsystem and a alright ft2 thing (that is far too long)

Diffstat:
Minclude/abi.h | 4++++
Ainclude/audio.h | 36++++++++++++++++++++++++++++++++++++
Ainclude/ft2.h | 13+++++++++++++
Asrc/audio.c | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ft2.c | 8418+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.c | 5+++++
Msrc/scene.c | 39++++++++++++++++++++++++++++-----------
Msrc/syscall.c | 38++++++++++++++++++++++++++++++++++----
8 files changed, 8776 insertions(+), 15 deletions(-)

diff --git a/include/abi.h b/include/abi.h @@ -22,5 +22,9 @@ #define TA_SYS_SHAPES_RECTANGLE 18 #define TA_SYS_SHAPES_TRIANGLE 19 #define TA_SYS_SHAPES_OVAL 20 +#define TA_SYS_FT2_LOAD 21 +#define TA_SYS_FT2_PLAY 22 +#define TA_SYS_FT2_STOP 23 +#define TA_SYS_FT2_IS_PLAYING 24 #endif // TA_ABI_H diff --git a/include/audio.h b/include/audio.h @@ -0,0 +1,36 @@ +#ifndef TA_AUDIO_H +#define TA_AUDIO_H + +#include <stdint.h> +#include <stdbool.h> + +#define TA_AUDIO_BUFFERS 16 +#define TA_AUDIO_RATE 44100 + +#ifndef TA_AUDIO_BUFFER_SIZE +#define TA_AUDIO_BUFFER_SIZE 2048 +#endif + +#define TA_AUDIO_VOLUME_MAX 255 +#define TA_AUDIO_PAN_LEFT 0 +#define TA_AUDIO_PAN_CENTER 128 +#define TA_AUDIO_PAN_RIGHT 255 + +typedef void (*ta_audio_source)(void *user); + +int ta_audio_lock(ta_audio_source source, void *user); +void ta_audio_unlock(int buffer); +bool ta_audio_is_locked(int buffer); +void ta_audio_set_volume(int buffer, uint8_t volume); +void ta_audio_set_pan(int buffer, uint8_t pan); +uint8_t ta_audio_get_volume(int buffer); +uint8_t ta_audio_get_pan(int buffer); +uint32_t ta_audio_free(int buffer); +uint32_t ta_audio_write(int buffer, const int8_t *samples, uint32_t count); +uint32_t ta_audio_available(int buffer); +uint32_t ta_audio_read(int buffer, int8_t *samples, uint32_t count); +void ta_audio_clear(int buffer); +void ta_audio_update(void); +uint32_t ta_audio_mix(int16_t *out, uint32_t frames); + +#endif // TA_AUDIO_H diff --git a/include/ft2.h b/include/ft2.h @@ -0,0 +1,13 @@ +#ifndef TA_FT2_H +#define TA_FT2_H + +#include <stdint.h> +#include <stdbool.h> + +bool ta_ft2_init(const uint8_t *data, uint32_t length); +bool ta_ft2_play(void); +void ta_ft2_stop(void); +bool ta_ft2_is_playing(void); +void ta_ft2_destroy(void); + +#endif // TA_FT2_H diff --git a/src/audio.c b/src/audio.c @@ -0,0 +1,238 @@ +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#include "audio.h" + +#define TA_AUDIO_MASK (TA_AUDIO_BUFFER_SIZE - 1) +#define TA_AUDIO_MIX_CHUNK 256 + +typedef char ta_audio_size_is_power_of_two[(TA_AUDIO_BUFFER_SIZE & TA_AUDIO_MASK) == 0 ? 1 : -1]; + +typedef struct { + int8_t data[TA_AUDIO_BUFFER_SIZE]; + uint32_t read; + uint32_t write; + ta_audio_source source; + void *user; + uint8_t volume; + uint8_t pan; + bool locked; +} ta_audio_buffer; + +static ta_audio_buffer buffers[TA_AUDIO_BUFFERS]; +static int32_t mix_left[TA_AUDIO_MIX_CHUNK]; +static int32_t mix_right[TA_AUDIO_MIX_CHUNK]; + +static bool buffer_valid(int buffer) { + return buffer >= 0 && buffer < TA_AUDIO_BUFFERS && buffers[buffer].locked; +} + +int ta_audio_lock(ta_audio_source source, void *user) { + for (int i = 0; i < TA_AUDIO_BUFFERS; i++) { + if (!buffers[i].locked) { + buffers[i].read = 0; + buffers[i].write = 0; + buffers[i].source = source; + buffers[i].user = user; + buffers[i].volume = TA_AUDIO_VOLUME_MAX; + buffers[i].pan = TA_AUDIO_PAN_CENTER; + buffers[i].locked = true; + + return i; + } + } + + return -1; +} + +void ta_audio_unlock(int buffer) { + if (!buffer_valid(buffer)) { + return; + } + + buffers[buffer].locked = false; + buffers[buffer].source = NULL; + buffers[buffer].user = NULL; + buffers[buffer].read = 0; + buffers[buffer].write = 0; +} + +bool ta_audio_is_locked(int buffer) { + return buffer_valid(buffer); +} + +void ta_audio_set_volume(int buffer, uint8_t volume) { + if (buffer_valid(buffer)) { + buffers[buffer].volume = volume; + } +} + +void ta_audio_set_pan(int buffer, uint8_t pan) { + if (buffer_valid(buffer)) { + buffers[buffer].pan = pan; + } +} + +uint8_t ta_audio_get_volume(int buffer) { + return buffer_valid(buffer) ? buffers[buffer].volume : 0; +} + +uint8_t ta_audio_get_pan(int buffer) { + return buffer_valid(buffer) ? buffers[buffer].pan : TA_AUDIO_PAN_CENTER; +} + +uint32_t ta_audio_available(int buffer) { + if (!buffer_valid(buffer)) { + return 0; + } + + return buffers[buffer].write - buffers[buffer].read; +} + +uint32_t ta_audio_free(int buffer) { + if (!buffer_valid(buffer)) { + return 0; + } + + return TA_AUDIO_BUFFER_SIZE - (buffers[buffer].write - buffers[buffer].read); +} + +uint32_t ta_audio_write(int buffer, const int8_t *samples, uint32_t count) { + uint32_t room = ta_audio_free(buffer); + + if (samples == NULL || room == 0) { + return 0; + } + + if (count > room) { + count = room; + } + + ta_audio_buffer *target = &buffers[buffer]; + + for (uint32_t i = 0; i < count; i++) { + target->data[(target->write + i) & TA_AUDIO_MASK] = samples[i]; + } + + target->write += count; + + return count; +} + +uint32_t ta_audio_read(int buffer, int8_t *samples, uint32_t count) { + uint32_t ready = ta_audio_available(buffer); + + if (samples == NULL || ready == 0) { + return 0; + } + + if (count > ready) { + count = ready; + } + + ta_audio_buffer *source = &buffers[buffer]; + + for (uint32_t i = 0; i < count; i++) { + samples[i] = source->data[(source->read + i) & TA_AUDIO_MASK]; + } + + source->read += count; + + return count; +} + +void ta_audio_clear(int buffer) { + if (buffer_valid(buffer)) { + buffers[buffer].read = buffers[buffer].write; + } +} + +void ta_audio_update(void) { + for (int i = 0; i < TA_AUDIO_BUFFERS; i++) { + if (!buffers[i].locked || buffers[i].source == NULL) { + continue; + } + + bool already_called = false; + + for (int j = 0; j < i; j++) { + if (buffers[j].locked && buffers[j].source == buffers[i].source && buffers[j].user == buffers[i].user) { + already_called = true; + break; + } + } + + if (!already_called) { + buffers[i].source(buffers[i].user); + } + } +} + +static int16_t clamp16(int32_t value) { + if (value > 32767) { + return 32767; + } + + if (value < -32768) { + return -32768; + } + + return (int16_t)value; +} + +uint32_t ta_audio_mix(int16_t *out, uint32_t frames) { + if (out == NULL) { + return 0; + } + + uint32_t done = 0; + + while (done < frames) { + uint32_t chunk = frames - done; + + if (chunk > TA_AUDIO_MIX_CHUNK) { + chunk = TA_AUDIO_MIX_CHUNK; + } + + for (uint32_t i = 0; i < chunk; i++) { + mix_left[i] = 0; + mix_right[i] = 0; + } + + for (int b = 0; b < TA_AUDIO_BUFFERS; b++) { + if (!buffers[b].locked || buffers[b].volume == 0) { + continue; + } + + ta_audio_buffer *source = &buffers[b]; + uint32_t ready = source->write - source->read; + + if (ready > chunk) { + ready = chunk; + } + + int32_t gain_right = source->pan; + int32_t gain_left = 255 - gain_right; + int32_t left_scale = (int32_t)source->volume * gain_left; + int32_t right_scale = (int32_t)source->volume * gain_right; + + for (uint32_t i = 0; i < ready; i++) { + int32_t sample = source->data[(source->read + i) & TA_AUDIO_MASK]; + + mix_left[i] += (sample * left_scale) >> 8; + mix_right[i] += (sample * right_scale) >> 8; + } + + source->read += ready; + } + + for (uint32_t i = 0; i < chunk; i++) { + out[(done + i) * 2] = clamp16(mix_left[i]); + out[(done + i) * 2 + 1] = clamp16(mix_right[i]); + } + + done += chunk; + } + + return done; +} diff --git a/src/ft2.c b/src/ft2.c @@ -0,0 +1,8418 @@ +/* +Derived from ft2-clone v2.28 + +BSD 3-Clause License + +Copyright (c) 2016-2026, Olav Sørensen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include "audio.h" +#include "ft2.h" + +#define MIN_BPM 32 +#define MAX_BPM 255 +#define MAX_SPEED 31 +#define MAX_CHANNELS 32 +#define NOTE_C4 (4*12) +#define NOTE_OFF 97 +#define MAX_NOTES ((10*12*16)+16) +#define MAX_PATTERNS 256 +#define MAX_PATT_LEN 256 +#define MAX_INST 128 +#define MAX_SMP_PER_INST 16 +#define MAX_ORDERS 256 +#define MAX_SAMPLE_LEN 0x3FFFFFFF +#define INSTR_HEADER_SIZE 263 +#define FREQUENCY_MUL_FACTOR 3181584u +#define FREQUENCY_DIV_FACTOR 21276843u +#define SAMPLES_PER_TICK_NUMERATOR 110250u +#define QUICK_VOLRAMP_SAMPLES 220u +#define MAX_VOICE_DELTA 0x0FFFFFFFu +#define MIX_CHUNK 256 +#define OUTPUT_SHIFT 10 + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +enum { + CS_UPDATE_VOL = 1, + CF_UPDATE_PERIOD = 2, + CS_TRIGGER_VOICE = 4, + CS_UPDATE_PAN = 8, + CS_USE_QUICK_VOLRAMP = 16, + LOOP_DISABLED = 0, + LOOP_FORWARD = 1, + LOOP_PINGPONG = 2, + SAMPLE_16BIT = 16, + SAMPLE_STEREO = 32, + SAMPLE_ADPCM = 64, + ENV_ENABLED = 1, + ENV_SUSTAIN = 2, + ENV_LOOP = 4 +}; + +#define GET_LOOPTYPE(smp_flags) ((smp_flags) & (LOOP_FORWARD | LOOP_PINGPONG)) +#define DISABLE_LOOP(smp_flags) ((smp_flags) &= ~(LOOP_FORWARD | LOOP_PINGPONG)) + +typedef struct note_t { + uint8_t note; + uint8_t instr; + uint8_t vol; + uint8_t efx; + uint8_t efx_data; +} note_t; + +typedef struct sample_t { + int8_t *data_ptr; + int8_t finetune; + int8_t relative_note; + uint8_t volume; + uint8_t flags; + uint8_t panning; + int32_t length; + int32_t loop_start; + int32_t loop_length; +} sample_t; + +typedef struct instr_t { + bool mute; + uint8_t note2_sample_lut[96]; + uint8_t vol_env_length; + uint8_t pan_env_length; + uint8_t vol_env_sustain; + uint8_t vol_env_loop_start; + uint8_t vol_env_loop_end; + uint8_t pan_env_sustain; + uint8_t pan_env_loop_start; + uint8_t pan_env_loop_end; + uint8_t vol_env_flags; + uint8_t pan_env_flags; + uint8_t auto_vib_type; + uint8_t auto_vib_sweep; + uint8_t auto_vib_depth; + uint8_t auto_vib_rate; + uint16_t fadeout; + int16_t vol_env_points[12][2]; + int16_t pan_env_points[12][2]; + int16_t num_samples; + sample_t smp[MAX_SMP_PER_INST]; +} instr_t; + +typedef struct channel_t { + bool key_off; + bool channel_off; + bool mute; + bool semitone_porta_mode; + uint8_t status; + int8_t relative_note; + int8_t finetune; + uint8_t smp_num; + uint8_t instr_num; + uint8_t efx_data; + uint8_t efx; + uint8_t sample_offset; + uint8_t tremor_param; + uint8_t tremor_pos; + uint8_t glob_vol_slide_speed; + uint8_t panning_slide_speed; + uint8_t vib_trem_ctrl; + uint8_t portamento_direction; + uint8_t vibrato_pos; + uint8_t tremolo_pos; + uint8_t vibrato_speed; + uint8_t vibrato_depth; + uint8_t tremolo_speed; + uint8_t tremolo_depth; + uint8_t pattern_loop_start_row; + uint8_t pattern_loop_counter; + uint8_t vol_slide_speed; + uint8_t f_vol_slide_up_speed; + uint8_t f_vol_slide_down_speed; + uint8_t f_pitch_slide_up_speed; + uint8_t f_pitch_slide_down_speed; + uint8_t ef_pitch_slide_up_speed; + uint8_t ef_pitch_slide_down_speed; + uint8_t pitch_slide_up_speed; + uint8_t pitch_slide_down_speed; + uint8_t note_retrig_speed; + uint8_t note_retrig_counter; + uint8_t note_retrig_vol; + uint8_t vol_column_vol; + uint8_t note_num; + uint8_t pan_env_pos; + uint8_t auto_vib_pos; + uint8_t vol_env_pos; + uint8_t real_vol; + uint8_t old_vol; + uint8_t out_vol; + uint8_t old_pan; + uint8_t out_pan; + uint8_t final_pan; + int16_t vol_env_delta; + int16_t vol_env_value; + int16_t pan_env_delta; + int16_t pan_env_value; + uint16_t out_period; + uint16_t real_period; + uint16_t final_period; + uint16_t copy_of_instr_and_note; + uint16_t portamento_target_period; + uint16_t portamento_speed; + uint16_t vol_env_tick; + uint16_t pan_env_tick; + uint16_t auto_vib_amp; + uint16_t auto_vib_sweep; + uint16_t fadeout_vol; + uint16_t fadeout_speed; + uint16_t final_vol; + int32_t smp_start_pos; + sample_t *smp_ptr; + instr_t *instr_ptr; +} channel_t; + +typedef struct song_t { + bool p_break_flag; + bool pos_jump_flag; + uint8_t patt_del_time; + uint8_t patt_del_time2; + uint8_t p_break_pos; + uint8_t orders[MAX_ORDERS]; + int16_t song_pos; + int16_t patt_num; + int16_t row; + int16_t curr_num_rows; + uint16_t song_length; + uint16_t song_loop_start; + uint16_t bpm; + uint16_t speed; + uint16_t initial_speed; + uint16_t initial_bpm; + uint16_t global_volume; + uint16_t tick; + int32_t num_channels; +} song_t; + +typedef void (*efx_routine)(channel_t *ch, uint8_t param); +typedef void (*vol_column_efx_routine)(channel_t *ch); +typedef void (*vol_column_efx_routine2)(channel_t *ch, uint8_t *vol_column_data); + +typedef struct voice_t { + const int8_t *data; + bool active; + bool is_16bit; + bool sampling_backwards; + bool is_fade_out_voice; + uint8_t loop_type; + uint8_t panning; + uint16_t volume; + uint32_t delta; + uint32_t position; + uint32_t position_frac; + uint32_t sample_end; + uint32_t loop_start; + uint32_t loop_length; + int32_t curr_volume_l; + int32_t curr_volume_r; + int32_t target_volume_l; + int32_t target_volume_r; + int32_t volume_delta_l; + int32_t volume_delta_r; + uint32_t volume_ramp_length; +} voice_t; + +static song_t song; +static channel_t channel[MAX_CHANNELS]; +static instr_t *instr[MAX_INST + 1]; +static note_t *pattern[MAX_PATTERNS]; +static int16_t pattern_num_rows[MAX_PATTERNS]; +static const uint16_t *note2_period_lut; +static bool linear_periods_flag; +static bool bxx_overflow; +static bool song_loaded; +static bool song_playing; +static const note_t nil_pattern_line[MAX_CHANNELS]; + +static voice_t voice[MAX_CHANNELS * 2]; +static uint32_t panning_table[256 + 1]; +static bool panning_table_ready; +static uint32_t samples_per_tick; +static uint32_t mixer_bpm = 125; +static uint32_t tick_remainder; +static uint32_t tick_samples_left; +static int audio_left = -1; +static int audio_right = -1; +static uint32_t dither_state = 0x12345000u; +static int32_t mix_l[MIX_CHUNK], mix_r[MIX_CHUNK]; +static int8_t out_l[MIX_CHUNK], out_r[MIX_CHUNK]; + +static void set_mixer_bpm(int32_t bpm) { + if (bpm < MIN_BPM || bpm > MAX_BPM) { + return; + } + + mixer_bpm = (uint32_t)bpm; +} + +static const uint8_t arpeggio_tab[32] = { + 0, + 1, + 2, + 0, + 1, + 2, + 0, + 1, + 2, + 0, + 1, + 2, + 0, + 1, + 2, + 0, + 0x00, + 0x18, + 0x31, + 0x4A, + 0x61, + 0x78, + 0x8D, + 0xA1, + 0xB4, + 0xC5, + 0xD4, + 0xE0, + 0xEB, + 0xF4, + 0xFA, + 0xFD +}; + +static const int8_t auto_vib_sine_tab[256] = { + 0, + -2, + -3, + -5, + -6, + -8, + -9, + -11, + -12, + -14, + -16, + -17, + -19, + -20, + -22, + -23, + -24, + -26, + -27, + -29, + -30, + -32, + -33, + -34, + -36, + -37, + -38, + -39, + -41, + -42, + -43, + -44, + -45, + -46, + -47, + -48, + -49, + -50, + -51, + -52, + -53, + -54, + -55, + -56, + -56, + -57, + -58, + -59, + -59, + -60, + -60, + -61, + -61, + -62, + -62, + -62, + -63, + -63, + -63, + -64, + -64, + -64, + -64, + -64, + -64, + -64, + -64, + -64, + -64, + -64, + -63, + -63, + -63, + -62, + -62, + -62, + -61, + -61, + -60, + -60, + -59, + -59, + -58, + -57, + -56, + -56, + -55, + -54, + -53, + -52, + -51, + -50, + -49, + -48, + -47, + -46, + -45, + -44, + -43, + -42, + -41, + -39, + -38, + -37, + -36, + -34, + -33, + -32, + -30, + -29, + -27, + -26, + -24, + -23, + -22, + -20, + -19, + -17, + -16, + -14, + -12, + -11, + -9, + -8, + -6, + -5, + -3, + -2, + 0, + 2, + 3, + 5, + 6, + 8, + 9, + 11, + 12, + 14, + 16, + 17, + 19, + 20, + 22, + 23, + 24, + 26, + 27, + 29, + 30, + 32, + 33, + 34, + 36, + 37, + 38, + 39, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 56, + 57, + 58, + 59, + 59, + 60, + 60, + 61, + 61, + 62, + 62, + 62, + 63, + 63, + 63, + 64, + 64, + 64, + 64, + 64, + 64, + 64, + 64, + 64, + 64, + 64, + 63, + 63, + 63, + 62, + 62, + 62, + 61, + 61, + 60, + 60, + 59, + 59, + 58, + 57, + 56, + 56, + 55, + 54, + 53, + 52, + 51, + 50, + 49, + 48, + 47, + 46, + 45, + 44, + 43, + 42, + 41, + 39, + 38, + 37, + 36, + 34, + 33, + 32, + 30, + 29, + 27, + 26, + 24, + 23, + 22, + 20, + 19, + 17, + 16, + 14, + 12, + 11, + 9, + 8, + 6, + 5, + 3, + 2 +}; + +static const uint8_t vibrato_tab[32] = { + 0, + 24, + 49, + 74, + 97, + 120, + 141, + 161, + 180, + 197, + 212, + 224, + 235, + 244, + 250, + 253, + 255, + 253, + 250, + 244, + 235, + 224, + 212, + 197, + 180, + 161, + 141, + 120, + 97, + 74, + 49, + 24 +}; + +static const uint16_t linear_period_lut[1936] = { + 7744, + 7740, + 7736, + 7732, + 7728, + 7724, + 7720, + 7716, + 7712, + 7708, + 7704, + 7700, + 7696, + 7692, + 7688, + 7684, + 7680, + 7676, + 7672, + 7668, + 7664, + 7660, + 7656, + 7652, + 7648, + 7644, + 7640, + 7636, + 7632, + 7628, + 7624, + 7620, + 7616, + 7612, + 7608, + 7604, + 7600, + 7596, + 7592, + 7588, + 7584, + 7580, + 7576, + 7572, + 7568, + 7564, + 7560, + 7556, + 7552, + 7548, + 7544, + 7540, + 7536, + 7532, + 7528, + 7524, + 7520, + 7516, + 7512, + 7508, + 7504, + 7500, + 7496, + 7492, + 7488, + 7484, + 7480, + 7476, + 7472, + 7468, + 7464, + 7460, + 7456, + 7452, + 7448, + 7444, + 7440, + 7436, + 7432, + 7428, + 7424, + 7420, + 7416, + 7412, + 7408, + 7404, + 7400, + 7396, + 7392, + 7388, + 7384, + 7380, + 7376, + 7372, + 7368, + 7364, + 7360, + 7356, + 7352, + 7348, + 7344, + 7340, + 7336, + 7332, + 7328, + 7324, + 7320, + 7316, + 7312, + 7308, + 7304, + 7300, + 7296, + 7292, + 7288, + 7284, + 7280, + 7276, + 7272, + 7268, + 7264, + 7260, + 7256, + 7252, + 7248, + 7244, + 7240, + 7236, + 7232, + 7228, + 7224, + 7220, + 7216, + 7212, + 7208, + 7204, + 7200, + 7196, + 7192, + 7188, + 7184, + 7180, + 7176, + 7172, + 7168, + 7164, + 7160, + 7156, + 7152, + 7148, + 7144, + 7140, + 7136, + 7132, + 7128, + 7124, + 7120, + 7116, + 7112, + 7108, + 7104, + 7100, + 7096, + 7092, + 7088, + 7084, + 7080, + 7076, + 7072, + 7068, + 7064, + 7060, + 7056, + 7052, + 7048, + 7044, + 7040, + 7036, + 7032, + 7028, + 7024, + 7020, + 7016, + 7012, + 7008, + 7004, + 7000, + 6996, + 6992, + 6988, + 6984, + 6980, + 6976, + 6972, + 6968, + 6964, + 6960, + 6956, + 6952, + 6948, + 6944, + 6940, + 6936, + 6932, + 6928, + 6924, + 6920, + 6916, + 6912, + 6908, + 6904, + 6900, + 6896, + 6892, + 6888, + 6884, + 6880, + 6876, + 6872, + 6868, + 6864, + 6860, + 6856, + 6852, + 6848, + 6844, + 6840, + 6836, + 6832, + 6828, + 6824, + 6820, + 6816, + 6812, + 6808, + 6804, + 6800, + 6796, + 6792, + 6788, + 6784, + 6780, + 6776, + 6772, + 6768, + 6764, + 6760, + 6756, + 6752, + 6748, + 6744, + 6740, + 6736, + 6732, + 6728, + 6724, + 6720, + 6716, + 6712, + 6708, + 6704, + 6700, + 6696, + 6692, + 6688, + 6684, + 6680, + 6676, + 6672, + 6668, + 6664, + 6660, + 6656, + 6652, + 6648, + 6644, + 6640, + 6636, + 6632, + 6628, + 6624, + 6620, + 6616, + 6612, + 6608, + 6604, + 6600, + 6596, + 6592, + 6588, + 6584, + 6580, + 6576, + 6572, + 6568, + 6564, + 6560, + 6556, + 6552, + 6548, + 6544, + 6540, + 6536, + 6532, + 6528, + 6524, + 6520, + 6516, + 6512, + 6508, + 6504, + 6500, + 6496, + 6492, + 6488, + 6484, + 6480, + 6476, + 6472, + 6468, + 6464, + 6460, + 6456, + 6452, + 6448, + 6444, + 6440, + 6436, + 6432, + 6428, + 6424, + 6420, + 6416, + 6412, + 6408, + 6404, + 6400, + 6396, + 6392, + 6388, + 6384, + 6380, + 6376, + 6372, + 6368, + 6364, + 6360, + 6356, + 6352, + 6348, + 6344, + 6340, + 6336, + 6332, + 6328, + 6324, + 6320, + 6316, + 6312, + 6308, + 6304, + 6300, + 6296, + 6292, + 6288, + 6284, + 6280, + 6276, + 6272, + 6268, + 6264, + 6260, + 6256, + 6252, + 6248, + 6244, + 6240, + 6236, + 6232, + 6228, + 6224, + 6220, + 6216, + 6212, + 6208, + 6204, + 6200, + 6196, + 6192, + 6188, + 6184, + 6180, + 6176, + 6172, + 6168, + 6164, + 6160, + 6156, + 6152, + 6148, + 6144, + 6140, + 6136, + 6132, + 6128, + 6124, + 6120, + 6116, + 6112, + 6108, + 6104, + 6100, + 6096, + 6092, + 6088, + 6084, + 6080, + 6076, + 6072, + 6068, + 6064, + 6060, + 6056, + 6052, + 6048, + 6044, + 6040, + 6036, + 6032, + 6028, + 6024, + 6020, + 6016, + 6012, + 6008, + 6004, + 6000, + 5996, + 5992, + 5988, + 5984, + 5980, + 5976, + 5972, + 5968, + 5964, + 5960, + 5956, + 5952, + 5948, + 5944, + 5940, + 5936, + 5932, + 5928, + 5924, + 5920, + 5916, + 5912, + 5908, + 5904, + 5900, + 5896, + 5892, + 5888, + 5884, + 5880, + 5876, + 5872, + 5868, + 5864, + 5860, + 5856, + 5852, + 5848, + 5844, + 5840, + 5836, + 5832, + 5828, + 5824, + 5820, + 5816, + 5812, + 5808, + 5804, + 5800, + 5796, + 5792, + 5788, + 5784, + 5780, + 5776, + 5772, + 5768, + 5764, + 5760, + 5756, + 5752, + 5748, + 5744, + 5740, + 5736, + 5732, + 5728, + 5724, + 5720, + 5716, + 5712, + 5708, + 5704, + 5700, + 5696, + 5692, + 5688, + 5684, + 5680, + 5676, + 5672, + 5668, + 5664, + 5660, + 5656, + 5652, + 5648, + 5644, + 5640, + 5636, + 5632, + 5628, + 5624, + 5620, + 5616, + 5612, + 5608, + 5604, + 5600, + 5596, + 5592, + 5588, + 5584, + 5580, + 5576, + 5572, + 5568, + 5564, + 5560, + 5556, + 5552, + 5548, + 5544, + 5540, + 5536, + 5532, + 5528, + 5524, + 5520, + 5516, + 5512, + 5508, + 5504, + 5500, + 5496, + 5492, + 5488, + 5484, + 5480, + 5476, + 5472, + 5468, + 5464, + 5460, + 5456, + 5452, + 5448, + 5444, + 5440, + 5436, + 5432, + 5428, + 5424, + 5420, + 5416, + 5412, + 5408, + 5404, + 5400, + 5396, + 5392, + 5388, + 5384, + 5380, + 5376, + 5372, + 5368, + 5364, + 5360, + 5356, + 5352, + 5348, + 5344, + 5340, + 5336, + 5332, + 5328, + 5324, + 5320, + 5316, + 5312, + 5308, + 5304, + 5300, + 5296, + 5292, + 5288, + 5284, + 5280, + 5276, + 5272, + 5268, + 5264, + 5260, + 5256, + 5252, + 5248, + 5244, + 5240, + 5236, + 5232, + 5228, + 5224, + 5220, + 5216, + 5212, + 5208, + 5204, + 5200, + 5196, + 5192, + 5188, + 5184, + 5180, + 5176, + 5172, + 5168, + 5164, + 5160, + 5156, + 5152, + 5148, + 5144, + 5140, + 5136, + 5132, + 5128, + 5124, + 5120, + 5116, + 5112, + 5108, + 5104, + 5100, + 5096, + 5092, + 5088, + 5084, + 5080, + 5076, + 5072, + 5068, + 5064, + 5060, + 5056, + 5052, + 5048, + 5044, + 5040, + 5036, + 5032, + 5028, + 5024, + 5020, + 5016, + 5012, + 5008, + 5004, + 5000, + 4996, + 4992, + 4988, + 4984, + 4980, + 4976, + 4972, + 4968, + 4964, + 4960, + 4956, + 4952, + 4948, + 4944, + 4940, + 4936, + 4932, + 4928, + 4924, + 4920, + 4916, + 4912, + 4908, + 4904, + 4900, + 4896, + 4892, + 4888, + 4884, + 4880, + 4876, + 4872, + 4868, + 4864, + 4860, + 4856, + 4852, + 4848, + 4844, + 4840, + 4836, + 4832, + 4828, + 4824, + 4820, + 4816, + 4812, + 4808, + 4804, + 4800, + 4796, + 4792, + 4788, + 4784, + 4780, + 4776, + 4772, + 4768, + 4764, + 4760, + 4756, + 4752, + 4748, + 4744, + 4740, + 4736, + 4732, + 4728, + 4724, + 4720, + 4716, + 4712, + 4708, + 4704, + 4700, + 4696, + 4692, + 4688, + 4684, + 4680, + 4676, + 4672, + 4668, + 4664, + 4660, + 4656, + 4652, + 4648, + 4644, + 4640, + 4636, + 4632, + 4628, + 4624, + 4620, + 4616, + 4612, + 4608, + 4604, + 4600, + 4596, + 4592, + 4588, + 4584, + 4580, + 4576, + 4572, + 4568, + 4564, + 4560, + 4556, + 4552, + 4548, + 4544, + 4540, + 4536, + 4532, + 4528, + 4524, + 4520, + 4516, + 4512, + 4508, + 4504, + 4500, + 4496, + 4492, + 4488, + 4484, + 4480, + 4476, + 4472, + 4468, + 4464, + 4460, + 4456, + 4452, + 4448, + 4444, + 4440, + 4436, + 4432, + 4428, + 4424, + 4420, + 4416, + 4412, + 4408, + 4404, + 4400, + 4396, + 4392, + 4388, + 4384, + 4380, + 4376, + 4372, + 4368, + 4364, + 4360, + 4356, + 4352, + 4348, + 4344, + 4340, + 4336, + 4332, + 4328, + 4324, + 4320, + 4316, + 4312, + 4308, + 4304, + 4300, + 4296, + 4292, + 4288, + 4284, + 4280, + 4276, + 4272, + 4268, + 4264, + 4260, + 4256, + 4252, + 4248, + 4244, + 4240, + 4236, + 4232, + 4228, + 4224, + 4220, + 4216, + 4212, + 4208, + 4204, + 4200, + 4196, + 4192, + 4188, + 4184, + 4180, + 4176, + 4172, + 4168, + 4164, + 4160, + 4156, + 4152, + 4148, + 4144, + 4140, + 4136, + 4132, + 4128, + 4124, + 4120, + 4116, + 4112, + 4108, + 4104, + 4100, + 4096, + 4092, + 4088, + 4084, + 4080, + 4076, + 4072, + 4068, + 4064, + 4060, + 4056, + 4052, + 4048, + 4044, + 4040, + 4036, + 4032, + 4028, + 4024, + 4020, + 4016, + 4012, + 4008, + 4004, + 4000, + 3996, + 3992, + 3988, + 3984, + 3980, + 3976, + 3972, + 3968, + 3964, + 3960, + 3956, + 3952, + 3948, + 3944, + 3940, + 3936, + 3932, + 3928, + 3924, + 3920, + 3916, + 3912, + 3908, + 3904, + 3900, + 3896, + 3892, + 3888, + 3884, + 3880, + 3876, + 3872, + 3868, + 3864, + 3860, + 3856, + 3852, + 3848, + 3844, + 3840, + 3836, + 3832, + 3828, + 3824, + 3820, + 3816, + 3812, + 3808, + 3804, + 3800, + 3796, + 3792, + 3788, + 3784, + 3780, + 3776, + 3772, + 3768, + 3764, + 3760, + 3756, + 3752, + 3748, + 3744, + 3740, + 3736, + 3732, + 3728, + 3724, + 3720, + 3716, + 3712, + 3708, + 3704, + 3700, + 3696, + 3692, + 3688, + 3684, + 3680, + 3676, + 3672, + 3668, + 3664, + 3660, + 3656, + 3652, + 3648, + 3644, + 3640, + 3636, + 3632, + 3628, + 3624, + 3620, + 3616, + 3612, + 3608, + 3604, + 3600, + 3596, + 3592, + 3588, + 3584, + 3580, + 3576, + 3572, + 3568, + 3564, + 3560, + 3556, + 3552, + 3548, + 3544, + 3540, + 3536, + 3532, + 3528, + 3524, + 3520, + 3516, + 3512, + 3508, + 3504, + 3500, + 3496, + 3492, + 3488, + 3484, + 3480, + 3476, + 3472, + 3468, + 3464, + 3460, + 3456, + 3452, + 3448, + 3444, + 3440, + 3436, + 3432, + 3428, + 3424, + 3420, + 3416, + 3412, + 3408, + 3404, + 3400, + 3396, + 3392, + 3388, + 3384, + 3380, + 3376, + 3372, + 3368, + 3364, + 3360, + 3356, + 3352, + 3348, + 3344, + 3340, + 3336, + 3332, + 3328, + 3324, + 3320, + 3316, + 3312, + 3308, + 3304, + 3300, + 3296, + 3292, + 3288, + 3284, + 3280, + 3276, + 3272, + 3268, + 3264, + 3260, + 3256, + 3252, + 3248, + 3244, + 3240, + 3236, + 3232, + 3228, + 3224, + 3220, + 3216, + 3212, + 3208, + 3204, + 3200, + 3196, + 3192, + 3188, + 3184, + 3180, + 3176, + 3172, + 3168, + 3164, + 3160, + 3156, + 3152, + 3148, + 3144, + 3140, + 3136, + 3132, + 3128, + 3124, + 3120, + 3116, + 3112, + 3108, + 3104, + 3100, + 3096, + 3092, + 3088, + 3084, + 3080, + 3076, + 3072, + 3068, + 3064, + 3060, + 3056, + 3052, + 3048, + 3044, + 3040, + 3036, + 3032, + 3028, + 3024, + 3020, + 3016, + 3012, + 3008, + 3004, + 3000, + 2996, + 2992, + 2988, + 2984, + 2980, + 2976, + 2972, + 2968, + 2964, + 2960, + 2956, + 2952, + 2948, + 2944, + 2940, + 2936, + 2932, + 2928, + 2924, + 2920, + 2916, + 2912, + 2908, + 2904, + 2900, + 2896, + 2892, + 2888, + 2884, + 2880, + 2876, + 2872, + 2868, + 2864, + 2860, + 2856, + 2852, + 2848, + 2844, + 2840, + 2836, + 2832, + 2828, + 2824, + 2820, + 2816, + 2812, + 2808, + 2804, + 2800, + 2796, + 2792, + 2788, + 2784, + 2780, + 2776, + 2772, + 2768, + 2764, + 2760, + 2756, + 2752, + 2748, + 2744, + 2740, + 2736, + 2732, + 2728, + 2724, + 2720, + 2716, + 2712, + 2708, + 2704, + 2700, + 2696, + 2692, + 2688, + 2684, + 2680, + 2676, + 2672, + 2668, + 2664, + 2660, + 2656, + 2652, + 2648, + 2644, + 2640, + 2636, + 2632, + 2628, + 2624, + 2620, + 2616, + 2612, + 2608, + 2604, + 2600, + 2596, + 2592, + 2588, + 2584, + 2580, + 2576, + 2572, + 2568, + 2564, + 2560, + 2556, + 2552, + 2548, + 2544, + 2540, + 2536, + 2532, + 2528, + 2524, + 2520, + 2516, + 2512, + 2508, + 2504, + 2500, + 2496, + 2492, + 2488, + 2484, + 2480, + 2476, + 2472, + 2468, + 2464, + 2460, + 2456, + 2452, + 2448, + 2444, + 2440, + 2436, + 2432, + 2428, + 2424, + 2420, + 2416, + 2412, + 2408, + 2404, + 2400, + 2396, + 2392, + 2388, + 2384, + 2380, + 2376, + 2372, + 2368, + 2364, + 2360, + 2356, + 2352, + 2348, + 2344, + 2340, + 2336, + 2332, + 2328, + 2324, + 2320, + 2316, + 2312, + 2308, + 2304, + 2300, + 2296, + 2292, + 2288, + 2284, + 2280, + 2276, + 2272, + 2268, + 2264, + 2260, + 2256, + 2252, + 2248, + 2244, + 2240, + 2236, + 2232, + 2228, + 2224, + 2220, + 2216, + 2212, + 2208, + 2204, + 2200, + 2196, + 2192, + 2188, + 2184, + 2180, + 2176, + 2172, + 2168, + 2164, + 2160, + 2156, + 2152, + 2148, + 2144, + 2140, + 2136, + 2132, + 2128, + 2124, + 2120, + 2116, + 2112, + 2108, + 2104, + 2100, + 2096, + 2092, + 2088, + 2084, + 2080, + 2076, + 2072, + 2068, + 2064, + 2060, + 2056, + 2052, + 2048, + 2044, + 2040, + 2036, + 2032, + 2028, + 2024, + 2020, + 2016, + 2012, + 2008, + 2004, + 2000, + 1996, + 1992, + 1988, + 1984, + 1980, + 1976, + 1972, + 1968, + 1964, + 1960, + 1956, + 1952, + 1948, + 1944, + 1940, + 1936, + 1932, + 1928, + 1924, + 1920, + 1916, + 1912, + 1908, + 1904, + 1900, + 1896, + 1892, + 1888, + 1884, + 1880, + 1876, + 1872, + 1868, + 1864, + 1860, + 1856, + 1852, + 1848, + 1844, + 1840, + 1836, + 1832, + 1828, + 1824, + 1820, + 1816, + 1812, + 1808, + 1804, + 1800, + 1796, + 1792, + 1788, + 1784, + 1780, + 1776, + 1772, + 1768, + 1764, + 1760, + 1756, + 1752, + 1748, + 1744, + 1740, + 1736, + 1732, + 1728, + 1724, + 1720, + 1716, + 1712, + 1708, + 1704, + 1700, + 1696, + 1692, + 1688, + 1684, + 1680, + 1676, + 1672, + 1668, + 1664, + 1660, + 1656, + 1652, + 1648, + 1644, + 1640, + 1636, + 1632, + 1628, + 1624, + 1620, + 1616, + 1612, + 1608, + 1604, + 1600, + 1596, + 1592, + 1588, + 1584, + 1580, + 1576, + 1572, + 1568, + 1564, + 1560, + 1556, + 1552, + 1548, + 1544, + 1540, + 1536, + 1532, + 1528, + 1524, + 1520, + 1516, + 1512, + 1508, + 1504, + 1500, + 1496, + 1492, + 1488, + 1484, + 1480, + 1476, + 1472, + 1468, + 1464, + 1460, + 1456, + 1452, + 1448, + 1444, + 1440, + 1436, + 1432, + 1428, + 1424, + 1420, + 1416, + 1412, + 1408, + 1404, + 1400, + 1396, + 1392, + 1388, + 1384, + 1380, + 1376, + 1372, + 1368, + 1364, + 1360, + 1356, + 1352, + 1348, + 1344, + 1340, + 1336, + 1332, + 1328, + 1324, + 1320, + 1316, + 1312, + 1308, + 1304, + 1300, + 1296, + 1292, + 1288, + 1284, + 1280, + 1276, + 1272, + 1268, + 1264, + 1260, + 1256, + 1252, + 1248, + 1244, + 1240, + 1236, + 1232, + 1228, + 1224, + 1220, + 1216, + 1212, + 1208, + 1204, + 1200, + 1196, + 1192, + 1188, + 1184, + 1180, + 1176, + 1172, + 1168, + 1164, + 1160, + 1156, + 1152, + 1148, + 1144, + 1140, + 1136, + 1132, + 1128, + 1124, + 1120, + 1116, + 1112, + 1108, + 1104, + 1100, + 1096, + 1092, + 1088, + 1084, + 1080, + 1076, + 1072, + 1068, + 1064, + 1060, + 1056, + 1052, + 1048, + 1044, + 1040, + 1036, + 1032, + 1028, + 1024, + 1020, + 1016, + 1012, + 1008, + 1004, + 1000, + 996, + 992, + 988, + 984, + 980, + 976, + 972, + 968, + 964, + 960, + 956, + 952, + 948, + 944, + 940, + 936, + 932, + 928, + 924, + 920, + 916, + 912, + 908, + 904, + 900, + 896, + 892, + 888, + 884, + 880, + 876, + 872, + 868, + 864, + 860, + 856, + 852, + 848, + 844, + 840, + 836, + 832, + 828, + 824, + 820, + 816, + 812, + 808, + 804, + 800, + 796, + 792, + 788, + 784, + 780, + 776, + 772, + 768, + 764, + 760, + 756, + 752, + 748, + 744, + 740, + 736, + 732, + 728, + 724, + 720, + 716, + 712, + 708, + 704, + 700, + 696, + 692, + 688, + 684, + 680, + 676, + 672, + 668, + 664, + 660, + 656, + 652, + 648, + 644, + 640, + 636, + 632, + 628, + 624, + 620, + 616, + 612, + 608, + 604, + 600, + 596, + 592, + 588, + 584, + 580, + 576, + 572, + 568, + 564, + 560, + 556, + 552, + 548, + 544, + 540, + 536, + 532, + 528, + 524, + 520, + 516, + 512, + 508, + 504, + 500, + 496, + 492, + 488, + 484, + 480, + 476, + 472, + 468, + 464, + 460, + 456, + 452, + 448, + 444, + 440, + 436, + 432, + 428, + 424, + 420, + 416, + 412, + 408, + 404, + 400, + 396, + 392, + 388, + 384, + 380, + 376, + 372, + 368, + 364, + 360, + 356, + 352, + 348, + 344, + 340, + 336, + 332, + 328, + 324, + 320, + 316, + 312, + 308, + 304, + 300, + 296, + 292, + 288, + 284, + 280, + 276, + 272, + 268, + 264, + 260, + 256, + 252, + 248, + 244, + 240, + 236, + 232, + 228, + 224, + 220, + 216, + 212, + 208, + 204, + 200, + 196, + 192, + 188, + 184, + 180, + 176, + 172, + 168, + 164, + 160, + 156, + 152, + 148, + 144, + 140, + 136, + 132, + 128, + 124, + 120, + 116, + 112, + 108, + 104, + 100, + 96, + 92, + 88, + 84, + 80, + 76, + 72, + 68, + 64, + 60, + 56, + 52, + 48, + 44, + 40, + 36, + 32, + 28, + 24, + 20, + 16, + 12, + 8, + 4 +}; + +static const uint16_t amiga_period_lut[1936] = { + 29024, + 28912, + 28800, + 28704, + 28608, + 28496, + 28384, + 28288, + 28192, + 28096, + 28000, + 27888, + 27776, + 27680, + 27584, + 27488, + 27392, + 27296, + 27200, + 27104, + 27008, + 26912, + 26816, + 26720, + 26624, + 26528, + 26432, + 26336, + 26240, + 26144, + 26048, + 25952, + 25856, + 25760, + 25664, + 25568, + 25472, + 25392, + 25312, + 25216, + 25120, + 25024, + 24928, + 24848, + 24768, + 24672, + 24576, + 24480, + 24384, + 24304, + 24224, + 24144, + 24064, + 23968, + 23872, + 23792, + 23712, + 23632, + 23552, + 23456, + 23360, + 23280, + 23200, + 23120, + 23040, + 22960, + 22880, + 22784, + 22688, + 22608, + 22528, + 22448, + 22368, + 22288, + 22208, + 22128, + 22048, + 21968, + 21888, + 21792, + 21696, + 21648, + 21600, + 21520, + 21440, + 21360, + 21280, + 21200, + 21120, + 21040, + 20960, + 20896, + 20832, + 20752, + 20672, + 20576, + 20480, + 20416, + 20352, + 20288, + 20224, + 20160, + 20096, + 20016, + 19936, + 19872, + 19808, + 19728, + 19648, + 19584, + 19520, + 19424, + 19328, + 19280, + 19232, + 19168, + 19104, + 19024, + 18944, + 18880, + 18816, + 18752, + 18688, + 18624, + 18560, + 18480, + 18400, + 18320, + 18240, + 18192, + 18144, + 18080, + 18016, + 17952, + 17888, + 17824, + 17760, + 17696, + 17632, + 17568, + 17504, + 17440, + 17376, + 17296, + 17216, + 17168, + 17120, + 17072, + 17024, + 16960, + 16896, + 16832, + 16768, + 16704, + 16640, + 16576, + 16512, + 16464, + 16416, + 16336, + 16256, + 16208, + 16160, + 16112, + 16064, + 16000, + 15936, + 15872, + 15808, + 15760, + 15712, + 15648, + 15584, + 15536, + 15488, + 15424, + 15360, + 15312, + 15264, + 15216, + 15168, + 15104, + 15040, + 14992, + 14944, + 14880, + 14816, + 14768, + 14720, + 14672, + 14624, + 14568, + 14512, + 14456, + 14400, + 14352, + 14304, + 14248, + 14192, + 14144, + 14096, + 14048, + 14000, + 13944, + 13888, + 13840, + 13792, + 13744, + 13696, + 13648, + 13600, + 13552, + 13504, + 13456, + 13408, + 13360, + 13312, + 13264, + 13216, + 13168, + 13120, + 13072, + 13024, + 12976, + 12928, + 12880, + 12832, + 12784, + 12736, + 12696, + 12656, + 12608, + 12560, + 12512, + 12464, + 12424, + 12384, + 12336, + 12288, + 12240, + 12192, + 12152, + 12112, + 12072, + 12032, + 11984, + 11936, + 11896, + 11856, + 11816, + 11776, + 11728, + 11680, + 11640, + 11600, + 11560, + 11520, + 11480, + 11440, + 11392, + 11344, + 11304, + 11264, + 11224, + 11184, + 11144, + 11104, + 11064, + 11024, + 10984, + 10944, + 10896, + 10848, + 10824, + 10800, + 10760, + 10720, + 10680, + 10640, + 10600, + 10560, + 10520, + 10480, + 10448, + 10416, + 10376, + 10336, + 10288, + 10240, + 10208, + 10176, + 10144, + 10112, + 10080, + 10048, + 10008, + 9968, + 9936, + 9904, + 9864, + 9824, + 9792, + 9760, + 9712, + 9664, + 9640, + 9616, + 9584, + 9552, + 9512, + 9472, + 9440, + 9408, + 9376, + 9344, + 9312, + 9280, + 9240, + 9200, + 9160, + 9120, + 9096, + 9072, + 9040, + 9008, + 8976, + 8944, + 8912, + 8880, + 8848, + 8816, + 8784, + 8752, + 8720, + 8688, + 8648, + 8608, + 8584, + 8560, + 8536, + 8512, + 8480, + 8448, + 8416, + 8384, + 8352, + 8320, + 8288, + 8256, + 8232, + 8208, + 8168, + 8128, + 8104, + 8080, + 8056, + 8032, + 8000, + 7968, + 7936, + 7904, + 7880, + 7856, + 7824, + 7792, + 7768, + 7744, + 7712, + 7680, + 7656, + 7632, + 7608, + 7584, + 7552, + 7520, + 7496, + 7472, + 7440, + 7408, + 7384, + 7360, + 7336, + 7312, + 7284, + 7256, + 7228, + 7200, + 7176, + 7152, + 7124, + 7096, + 7072, + 7048, + 7024, + 7000, + 6972, + 6944, + 6920, + 6896, + 6872, + 6848, + 6824, + 6800, + 6776, + 6752, + 6728, + 6704, + 6680, + 6656, + 6632, + 6608, + 6584, + 6560, + 6536, + 6512, + 6488, + 6464, + 6440, + 6416, + 6392, + 6368, + 6348, + 6328, + 6304, + 6280, + 6256, + 6232, + 6212, + 6192, + 6168, + 6144, + 6120, + 6096, + 6076, + 6056, + 6036, + 6016, + 5992, + 5968, + 5948, + 5928, + 5908, + 5888, + 5864, + 5840, + 5820, + 5800, + 5780, + 5760, + 5740, + 5720, + 5696, + 5672, + 5652, + 5632, + 5612, + 5592, + 5572, + 5552, + 5532, + 5512, + 5492, + 5472, + 5448, + 5424, + 5412, + 5400, + 5380, + 5360, + 5340, + 5320, + 5300, + 5280, + 5260, + 5240, + 5224, + 5208, + 5188, + 5168, + 5144, + 5120, + 5104, + 5088, + 5072, + 5056, + 5040, + 5024, + 5004, + 4984, + 4968, + 4952, + 4932, + 4912, + 4896, + 4880, + 4856, + 4832, + 4820, + 4808, + 4792, + 4776, + 4756, + 4736, + 4720, + 4704, + 4688, + 4672, + 4656, + 4640, + 4620, + 4600, + 4580, + 4560, + 4548, + 4536, + 4520, + 4504, + 4488, + 4472, + 4456, + 4440, + 4424, + 4408, + 4392, + 4376, + 4360, + 4344, + 4324, + 4304, + 4292, + 4280, + 4268, + 4256, + 4240, + 4224, + 4208, + 4192, + 4176, + 4160, + 4144, + 4128, + 4116, + 4104, + 4084, + 4064, + 4052, + 4040, + 4028, + 4016, + 4000, + 3984, + 3968, + 3952, + 3940, + 3928, + 3912, + 3896, + 3884, + 3872, + 3856, + 3840, + 3828, + 3816, + 3804, + 3792, + 3776, + 3760, + 3748, + 3736, + 3720, + 3704, + 3692, + 3680, + 3668, + 3656, + 3642, + 3628, + 3614, + 3600, + 3588, + 3576, + 3562, + 3548, + 3536, + 3524, + 3512, + 3500, + 3486, + 3472, + 3460, + 3448, + 3436, + 3424, + 3412, + 3400, + 3388, + 3376, + 3364, + 3352, + 3340, + 3328, + 3316, + 3304, + 3292, + 3280, + 3268, + 3256, + 3244, + 3232, + 3220, + 3208, + 3196, + 3184, + 3174, + 3164, + 3152, + 3140, + 3128, + 3116, + 3106, + 3096, + 3084, + 3072, + 3060, + 3048, + 3038, + 3028, + 3018, + 3008, + 2996, + 2984, + 2974, + 2964, + 2954, + 2944, + 2932, + 2920, + 2910, + 2900, + 2890, + 2880, + 2870, + 2860, + 2848, + 2836, + 2826, + 2816, + 2806, + 2796, + 2786, + 2776, + 2766, + 2756, + 2746, + 2736, + 2724, + 2712, + 2706, + 2700, + 2690, + 2680, + 2670, + 2660, + 2650, + 2640, + 2630, + 2620, + 2612, + 2604, + 2594, + 2584, + 2572, + 2560, + 2552, + 2544, + 2536, + 2528, + 2520, + 2512, + 2502, + 2492, + 2484, + 2476, + 2466, + 2456, + 2448, + 2440, + 2428, + 2416, + 2410, + 2404, + 2396, + 2388, + 2378, + 2368, + 2360, + 2352, + 2344, + 2336, + 2328, + 2320, + 2310, + 2300, + 2290, + 2280, + 2274, + 2268, + 2260, + 2252, + 2244, + 2236, + 2228, + 2220, + 2212, + 2204, + 2196, + 2188, + 2180, + 2172, + 2162, + 2152, + 2146, + 2140, + 2134, + 2128, + 2120, + 2112, + 2104, + 2096, + 2088, + 2080, + 2072, + 2064, + 2058, + 2052, + 2042, + 2032, + 2026, + 2020, + 2014, + 2008, + 2000, + 1992, + 1984, + 1976, + 1970, + 1964, + 1956, + 1948, + 1942, + 1936, + 1928, + 1920, + 1914, + 1908, + 1902, + 1896, + 1888, + 1880, + 1874, + 1868, + 1860, + 1852, + 1846, + 1840, + 1834, + 1828, + 1821, + 1814, + 1807, + 1800, + 1794, + 1788, + 1781, + 1774, + 1768, + 1762, + 1756, + 1750, + 1743, + 1736, + 1730, + 1724, + 1718, + 1712, + 1706, + 1700, + 1694, + 1688, + 1682, + 1676, + 1670, + 1664, + 1658, + 1652, + 1646, + 1640, + 1634, + 1628, + 1622, + 1616, + 1610, + 1604, + 1598, + 1592, + 1587, + 1582, + 1576, + 1570, + 1564, + 1558, + 1553, + 1548, + 1542, + 1536, + 1530, + 1524, + 1519, + 1514, + 1509, + 1504, + 1498, + 1492, + 1487, + 1482, + 1477, + 1472, + 1466, + 1460, + 1455, + 1450, + 1445, + 1440, + 1435, + 1430, + 1424, + 1418, + 1413, + 1408, + 1403, + 1398, + 1393, + 1388, + 1383, + 1378, + 1373, + 1368, + 1362, + 1356, + 1353, + 1350, + 1345, + 1340, + 1335, + 1330, + 1325, + 1320, + 1315, + 1310, + 1306, + 1302, + 1297, + 1292, + 1286, + 1280, + 1276, + 1272, + 1268, + 1264, + 1260, + 1256, + 1251, + 1246, + 1242, + 1238, + 1233, + 1228, + 1224, + 1220, + 1214, + 1208, + 1205, + 1202, + 1198, + 1194, + 1189, + 1184, + 1180, + 1176, + 1172, + 1168, + 1164, + 1160, + 1155, + 1150, + 1145, + 1140, + 1137, + 1134, + 1130, + 1126, + 1122, + 1118, + 1114, + 1110, + 1106, + 1102, + 1098, + 1094, + 1090, + 1086, + 1081, + 1076, + 1073, + 1070, + 1067, + 1064, + 1060, + 1056, + 1052, + 1048, + 1044, + 1040, + 1036, + 1032, + 1029, + 1026, + 1021, + 1016, + 1013, + 1010, + 1007, + 1004, + 1000, + 996, + 992, + 988, + 985, + 982, + 978, + 974, + 971, + 968, + 964, + 960, + 957, + 954, + 951, + 948, + 944, + 940, + 937, + 934, + 930, + 926, + 923, + 920, + 917, + 914, + 910, + 907, + 903, + 900, + 897, + 894, + 890, + 887, + 884, + 881, + 878, + 875, + 871, + 868, + 865, + 862, + 859, + 856, + 853, + 850, + 847, + 844, + 841, + 838, + 835, + 832, + 829, + 826, + 823, + 820, + 817, + 814, + 811, + 808, + 805, + 802, + 799, + 796, + 793, + 791, + 788, + 785, + 782, + 779, + 776, + 774, + 771, + 768, + 765, + 762, + 759, + 757, + 754, + 752, + 749, + 746, + 743, + 741, + 738, + 736, + 733, + 730, + 727, + 725, + 722, + 720, + 717, + 715, + 712, + 709, + 706, + 704, + 701, + 699, + 696, + 694, + 691, + 689, + 686, + 684, + 681, + 678, + 676, + 675, + 672, + 670, + 667, + 665, + 662, + 660, + 657, + 655, + 653, + 651, + 648, + 646, + 643, + 640, + 638, + 636, + 634, + 632, + 630, + 628, + 625, + 623, + 621, + 619, + 616, + 614, + 612, + 610, + 607, + 604, + 602, + 601, + 599, + 597, + 594, + 592, + 590, + 588, + 586, + 584, + 582, + 580, + 577, + 575, + 572, + 570, + 568, + 567, + 565, + 563, + 561, + 559, + 557, + 555, + 553, + 551, + 549, + 547, + 545, + 543, + 540, + 538, + 536, + 535, + 533, + 532, + 530, + 528, + 526, + 524, + 522, + 520, + 518, + 516, + 514, + 513, + 510, + 508, + 506, + 505, + 503, + 502, + 500, + 498, + 496, + 494, + 492, + 491, + 489, + 487, + 485, + 484, + 482, + 480, + 478, + 477, + 475, + 474, + 472, + 470, + 468, + 467, + 465, + 463, + 461, + 460, + 458, + 457, + 455, + 453, + 451, + 450, + 448, + 447, + 445, + 443, + 441, + 440, + 438, + 437, + 435, + 434, + 432, + 431, + 429, + 428, + 426, + 425, + 423, + 422, + 420, + 419, + 417, + 416, + 414, + 413, + 411, + 410, + 408, + 407, + 405, + 404, + 402, + 401, + 399, + 398, + 396, + 395, + 393, + 392, + 390, + 389, + 388, + 387, + 385, + 384, + 382, + 381, + 379, + 378, + 377, + 376, + 374, + 373, + 371, + 370, + 369, + 368, + 366, + 365, + 363, + 362, + 361, + 360, + 358, + 357, + 355, + 354, + 353, + 352, + 350, + 349, + 348, + 347, + 345, + 344, + 343, + 342, + 340, + 339, + 338, + 337, + 336, + 335, + 333, + 332, + 331, + 330, + 328, + 327, + 326, + 325, + 324, + 323, + 321, + 320, + 319, + 318, + 317, + 316, + 315, + 314, + 312, + 311, + 310, + 309, + 308, + 307, + 306, + 305, + 303, + 302, + 301, + 300, + 299, + 298, + 297, + 296, + 295, + 294, + 293, + 292, + 291, + 290, + 288, + 287, + 286, + 285, + 284, + 283, + 282, + 281, + 280, + 279, + 278, + 277, + 276, + 275, + 274, + 273, + 272, + 271, + 270, + 269, + 268, + 267, + 266, + 266, + 265, + 264, + 263, + 262, + 261, + 260, + 259, + 258, + 257, + 256, + 255, + 254, + 253, + 252, + 251, + 251, + 250, + 249, + 248, + 247, + 246, + 245, + 244, + 243, + 242, + 242, + 241, + 240, + 239, + 238, + 237, + 237, + 236, + 235, + 234, + 233, + 232, + 231, + 230, + 230, + 229, + 228, + 227, + 227, + 226, + 225, + 224, + 223, + 222, + 222, + 221, + 220, + 219, + 219, + 218, + 217, + 216, + 215, + 214, + 214, + 213, + 212, + 211, + 211, + 210, + 209, + 208, + 208, + 207, + 206, + 205, + 205, + 204, + 203, + 202, + 202, + 201, + 200, + 199, + 199, + 198, + 198, + 197, + 196, + 195, + 195, + 194, + 193, + 192, + 192, + 191, + 190, + 189, + 189, + 188, + 188, + 187, + 186, + 185, + 185, + 184, + 184, + 183, + 182, + 181, + 181, + 180, + 180, + 179, + 179, + 178, + 177, + 176, + 176, + 175, + 175, + 174, + 173, + 172, + 172, + 171, + 171, + 170, + 169, + 169, + 169, + 168, + 167, + 166, + 166, + 165, + 165, + 164, + 164, + 163, + 163, + 162, + 161, + 160, + 160, + 159, + 159, + 158, + 158, + 157, + 157, + 156, + 156, + 155, + 155, + 154, + 153, + 152, + 152, + 151, + 151, + 150, + 150, + 149, + 149, + 148, + 148, + 147, + 147, + 146, + 146, + 145, + 145, + 144, + 144, + 143, + 142, + 142, + 142, + 141, + 141, + 140, + 140, + 139, + 139, + 138, + 138, + 137, + 137, + 136, + 136, + 135, + 134, + 134, + 134, + 133, + 133, + 132, + 132, + 131, + 131, + 130, + 130, + 129, + 129, + 128, + 128, + 127, + 127, + 126, + 126, + 125, + 125, + 124, + 124, + 123, + 123, + 123, + 123, + 122, + 122, + 121, + 121, + 120, + 120, + 119, + 119, + 118, + 118, + 117, + 117, + 117, + 117, + 116, + 116, + 115, + 115, + 114, + 114, + 113, + 113, + 112, + 112, + 112, + 112, + 111, + 111, + 110, + 110, + 109, + 109, + 108, + 108, + 108, + 108, + 107, + 107, + 106, + 106, + 105, + 105, + 105, + 105, + 104, + 104, + 103, + 103, + 102, + 102, + 102, + 102, + 101, + 101, + 100, + 100, + 99, + 99, + 99, + 99, + 98, + 98, + 97, + 97, + 97, + 97, + 96, + 96, + 95, + 95, + 95, + 95, + 94, + 94, + 93, + 93, + 93, + 93, + 92, + 92, + 91, + 91, + 91, + 91, + 90, + 90, + 89, + 89, + 89, + 89, + 88, + 88, + 87, + 87, + 87, + 87, + 86, + 86, + 85, + 85, + 85, + 85, + 84, + 84, + 84, + 84, + 83, + 83, + 82, + 82, + 82, + 82, + 81, + 81, + 81, + 81, + 80, + 80, + 79, + 79, + 79, + 79, + 78, + 78, + 78, + 78, + 77, + 77, + 77, + 77, + 76, + 76, + 75, + 75, + 75, + 75, + 75, + 75, + 74, + 74, + 73, + 73, + 73, + 73, + 72, + 72, + 72, + 72, + 71, + 71, + 71, + 71, + 70, + 70, + 70, + 70, + 69, + 69, + 69, + 69, + 68, + 68, + 68, + 68, + 67, + 67, + 67, + 67, + 66, + 66, + 66, + 66, + 65, + 65, + 65, + 65, + 64, + 64, + 64, + 64, + 63, + 63, + 63, + 63, + 63, + 63, + 62, + 62, + 62, + 62, + 61, + 61, + 61, + 61, + 60, + 60, + 60, + 60, + 60, + 60, + 59, + 59, + 59, + 59, + 58, + 58, + 58, + 58, + 57, + 57, + 57, + 57, + 57, + 57, + 56, + 56, + 56, + 56, + 55, + 55, + 55, + 55, + 55, + 55, + 54, + 54, + 54, + 54, + 53, + 53, + 53, + 53, + 53, + 53, + 52, + 52, + 52, + 52, + 52, + 52, + 51, + 51, + 51, + 51, + 50, + 50, + 50, + 50, + 50, + 50, + 49, + 49, + 49, + 49, + 49, + 49, + 48, + 48, + 48, + 48, + 48, + 48, + 47, + 47, + 47, + 47, + 47, + 47, + 46, + 46, + 46, + 46, + 46, + 46, + 45, + 45, + 45, + 45, + 45, + 45, + 44, + 44, + 44, + 44, + 44, + 44, + 43, + 43, + 43, + 43, + 43, + 43, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 41, + 41, + 41, + 41, + 41, + 41, + 40, + 40, + 40, + 40, + 40, + 40, + 39, + 39, + 39, + 39, + 39, + 39, + 39, + 39, + 38, + 38, + 38, + 38, + 38, + 38, + 38, + 38, + 37, + 37, + 37, + 37, + 37, + 37, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 35, + 35, + 35, + 35, + 35, + 35, + 35, + 35, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 33, + 33, + 33, + 33, + 33, + 33, + 33, + 33, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 31, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 22, + 16, + 8, + 0, + 16, + 32, + 24, + 16, + 8, + 0, + 16, + 32, + 24, + 16, + 8, + 0, + 0 +}; + +static const uint32_t log_tab[4*12*16] = { + 16777216, + 16792365, + 16807527, + 16822704, + 16837894, + 16853097, + 16868315, + 16883546, + 16898791, + 16914049, + 16929322, + 16944608, + 16959908, + 16975222, + 16990549, + 17005891, + 17021246, + 17036615, + 17051999, + 17067396, + 17082806, + 17098231, + 17113670, + 17129123, + 17144589, + 17160070, + 17175564, + 17191073, + 17206595, + 17222132, + 17237683, + 17253247, + 17268826, + 17284419, + 17300026, + 17315646, + 17331282, + 17346931, + 17362594, + 17378271, + 17393963, + 17409669, + 17425389, + 17441123, + 17456871, + 17472634, + 17488410, + 17504202, + 17520007, + 17535826, + 17551660, + 17567508, + 17583371, + 17599248, + 17615139, + 17631044, + 17646964, + 17662898, + 17678847, + 17694810, + 17710787, + 17726779, + 17742785, + 17758806, + 17774841, + 17790891, + 17806955, + 17823034, + 17839127, + 17855235, + 17871357, + 17887494, + 17903645, + 17919811, + 17935992, + 17952187, + 17968397, + 17984621, + 18000860, + 18017114, + 18033382, + 18049665, + 18065963, + 18082276, + 18098603, + 18114945, + 18131302, + 18147673, + 18164060, + 18180461, + 18196877, + 18213307, + 18229753, + 18246213, + 18262689, + 18279179, + 18295684, + 18312204, + 18328739, + 18345288, + 18361853, + 18378433, + 18395028, + 18411637, + 18428262, + 18444902, + 18461556, + 18478226, + 18494911, + 18511611, + 18528325, + 18545056, + 18561801, + 18578561, + 18595336, + 18612127, + 18628932, + 18645753, + 18662589, + 18679441, + 18696307, + 18713189, + 18730086, + 18746998, + 18763925, + 18780868, + 18797826, + 18814800, + 18831788, + 18848792, + 18865812, + 18882846, + 18899897, + 18916962, + 18934043, + 18951139, + 18968251, + 18985378, + 19002521, + 19019679, + 19036853, + 19054042, + 19071247, + 19088467, + 19105703, + 19122954, + 19140221, + 19157504, + 19174802, + 19192116, + 19209445, + 19226790, + 19244151, + 19261527, + 19278919, + 19296327, + 19313750, + 19331190, + 19348645, + 19366115, + 19383602, + 19401104, + 19418622, + 19436156, + 19453706, + 19471271, + 19488853, + 19506450, + 19524063, + 19541692, + 19559337, + 19576998, + 19594675, + 19612368, + 19630077, + 19647802, + 19665543, + 19683300, + 19701072, + 19718861, + 19736666, + 19754488, + 19772325, + 19790178, + 19808047, + 19825933, + 19843835, + 19861752, + 19879686, + 19897637, + 19915603, + 19933586, + 19951585, + 19969600, + 19987631, + 20005679, + 20023743, + 20041823, + 20059920, + 20078033, + 20096162, + 20114308, + 20132470, + 20150648, + 20168843, + 20187054, + 20205282, + 20223526, + 20241787, + 20260064, + 20278358, + 20296668, + 20314995, + 20333338, + 20351698, + 20370074, + 20388467, + 20406877, + 20425303, + 20443746, + 20462206, + 20480682, + 20499175, + 20517684, + 20536211, + 20554754, + 20573313, + 20591890, + 20610483, + 20629093, + 20647720, + 20666364, + 20685025, + 20703702, + 20722396, + 20741107, + 20759835, + 20778580, + 20797342, + 20816121, + 20834917, + 20853729, + 20872559, + 20891406, + 20910270, + 20929150, + 20948048, + 20966963, + 20985895, + 21004844, + 21023810, + 21042794, + 21061794, + 21080812, + 21099846, + 21118898, + 21137968, + 21157054, + 21176158, + 21195278, + 21214417, + 21233572, + 21252745, + 21271935, + 21291142, + 21310367, + 21329609, + 21348868, + 21368145, + 21387439, + 21406751, + 21426080, + 21445426, + 21464790, + 21484172, + 21503571, + 21522987, + 21542421, + 21561873, + 21581342, + 21600829, + 21620333, + 21639855, + 21659395, + 21678952, + 21698527, + 21718119, + 21737729, + 21757357, + 21777003, + 21796666, + 21816348, + 21836046, + 21855763, + 21875498, + 21895250, + 21915020, + 21934808, + 21954614, + 21974438, + 21994279, + 22014139, + 22034016, + 22053912, + 22073825, + 22093757, + 22113706, + 22133674, + 22153659, + 22173663, + 22193684, + 22213724, + 22233781, + 22253857, + 22273951, + 22294063, + 22314194, + 22334342, + 22354509, + 22374693, + 22394897, + 22415118, + 22435357, + 22455615, + 22475891, + 22496186, + 22516499, + 22536830, + 22557179, + 22577547, + 22597933, + 22618338, + 22638761, + 22659202, + 22679662, + 22700141, + 22720638, + 22741153, + 22761687, + 22782240, + 22802811, + 22823400, + 22844009, + 22864635, + 22885281, + 22905945, + 22926628, + 22947329, + 22968049, + 22988788, + 23009546, + 23030322, + 23051117, + 23071931, + 23092764, + 23113615, + 23134485, + 23155374, + 23176282, + 23197209, + 23218155, + 23239120, + 23260103, + 23281106, + 23302127, + 23323168, + 23344227, + 23365306, + 23386403, + 23407520, + 23428656, + 23449810, + 23470984, + 23492177, + 23513389, + 23534620, + 23555871, + 23577140, + 23598429, + 23619737, + 23641065, + 23662411, + 23683777, + 23705162, + 23726566, + 23747990, + 23769433, + 23790896, + 23812377, + 23833879, + 23855399, + 23876939, + 23898499, + 23920078, + 23941676, + 23963294, + 23984932, + 24006589, + 24028265, + 24049962, + 24071677, + 24093413, + 24115168, + 24136942, + 24158736, + 24180550, + 24202384, + 24224237, + 24246111, + 24268003, + 24289916, + 24311848, + 24333801, + 24355773, + 24377765, + 24399776, + 24421808, + 24443859, + 24465931, + 24488022, + 24510133, + 24532265, + 24554416, + 24576587, + 24598778, + 24620990, + 24643221, + 24665472, + 24687744, + 24710036, + 24732347, + 24754679, + 24777031, + 24799403, + 24821796, + 24844209, + 24866641, + 24889095, + 24911568, + 24934062, + 24956576, + 24979110, + 25001665, + 25024240, + 25046835, + 25069451, + 25092088, + 25114744, + 25137421, + 25160119, + 25182837, + 25205576, + 25228335, + 25251115, + 25273915, + 25296736, + 25319578, + 25342440, + 25365322, + 25388226, + 25411150, + 25434095, + 25457060, + 25480047, + 25503054, + 25526081, + 25549130, + 25572199, + 25595290, + 25618401, + 25641533, + 25664686, + 25687859, + 25711054, + 25734270, + 25757506, + 25780764, + 25804042, + 25827342, + 25850662, + 25874004, + 25897367, + 25920751, + 25944156, + 25967582, + 25991029, + 26014497, + 26037987, + 26061498, + 26085030, + 26108583, + 26132158, + 26155754, + 26179371, + 26203009, + 26226669, + 26250350, + 26274053, + 26297777, + 26321522, + 26345289, + 26369077, + 26392887, + 26416718, + 26440571, + 26464445, + 26488341, + 26512259, + 26536198, + 26560158, + 26584141, + 26608145, + 26632170, + 26656218, + 26680287, + 26704377, + 26728490, + 26752624, + 26776780, + 26800958, + 26825158, + 26849380, + 26873623, + 26897888, + 26922176, + 26946485, + 26970816, + 26995169, + 27019544, + 27043941, + 27068360, + 27092802, + 27117265, + 27141750, + 27166258, + 27190787, + 27215339, + 27239913, + 27264509, + 27289127, + 27313768, + 27338430, + 27363116, + 27387823, + 27412552, + 27437304, + 27462079, + 27486875, + 27511695, + 27536536, + 27561400, + 27586286, + 27611195, + 27636126, + 27661080, + 27686057, + 27711056, + 27736077, + 27761121, + 27786188, + 27811277, + 27836389, + 27861524, + 27886681, + 27911861, + 27937064, + 27962290, + 27987538, + 28012809, + 28038103, + 28063420, + 28088760, + 28114122, + 28139508, + 28164916, + 28190347, + 28215802, + 28241279, + 28266779, + 28292302, + 28317849, + 28343418, + 28369011, + 28394626, + 28420265, + 28445927, + 28471612, + 28497320, + 28523052, + 28548806, + 28574584, + 28600385, + 28626210, + 28652058, + 28677929, + 28703823, + 28729741, + 28755683, + 28781647, + 28807636, + 28833647, + 28859682, + 28885741, + 28911823, + 28937929, + 28964058, + 28990211, + 29016388, + 29042588, + 29068811, + 29095059, + 29121330, + 29147625, + 29173944, + 29200286, + 29226652, + 29253042, + 29279456, + 29305894, + 29332355, + 29358841, + 29385350, + 29411883, + 29438441, + 29465022, + 29491627, + 29518256, + 29544910, + 29571587, + 29598288, + 29625014, + 29651764, + 29678538, + 29705336, + 29732158, + 29759004, + 29785875, + 29812770, + 29839689, + 29866633, + 29893600, + 29920593, + 29947609, + 29974650, + 30001716, + 30028805, + 30055920, + 30083059, + 30110222, + 30137410, + 30164622, + 30191859, + 30219120, + 30246407, + 30273717, + 30301053, + 30328413, + 30355798, + 30383207, + 30410642, + 30438101, + 30465584, + 30493093, + 30520627, + 30548185, + 30575768, + 30603377, + 30631010, + 30658668, + 30686351, + 30714059, + 30741792, + 30769550, + 30797333, + 30825141, + 30852975, + 30880833, + 30908717, + 30936625, + 30964559, + 30992519, + 31020503, + 31048513, + 31076548, + 31104608, + 31132694, + 31160805, + 31188941, + 31217103, + 31245290, + 31273503, + 31301741, + 31330005, + 31358294, + 31386609, + 31414949, + 31443315, + 31471707, + 31500124, + 31528567, + 31557035, + 31585529, + 31614049, + 31642595, + 31671166, + 31699764, + 31728387, + 31757036, + 31785710, + 31814411, + 31843138, + 31871890, + 31900669, + 31929473, + 31958304, + 31987160, + 32016043, + 32044951, + 32073886, + 32102847, + 32131834, + 32160847, + 32189887, + 32218952, + 32248044, + 32277162, + 32306307, + 32335478, + 32364675, + 32393898, + 32423148, + 32452424, + 32481727, + 32511056, + 32540412, + 32569794, + 32599202, + 32628638, + 32658099, + 32687588, + 32717103, + 32746645, + 32776213, + 32805808, + 32835430, + 32865078, + 32894754, + 32924456, + 32954184, + 32983940, + 33013723, + 33043532, + 33073369, + 33103232, + 33133122, + 33163040, + 33192984, + 33222955, + 33252954, + 33282979, + 33313032, + 33343112, + 33373219, + 33403353, + 33433514, + 33463703, + 33493919, + 33524162 +}; + +static void reset_volumes(channel_t *ch) { + ch->real_vol = ch->old_vol; + ch->out_vol = ch->old_vol; + ch->out_pan = ch->old_pan; + + ch->status |= CS_UPDATE_VOL + CS_UPDATE_PAN + CS_USE_QUICK_VOLRAMP; +} + +static void trigger_instrument(channel_t *ch) { + if (!(ch->vib_trem_ctrl & 0x04)) { + ch->vibrato_pos = 0; + } + + if (!(ch->vib_trem_ctrl & 0x40)) { + ch->tremolo_pos = 0; + } + + ch->note_retrig_counter = 0; + ch->tremor_pos = 0; + ch->key_off = false; + + instr_t *ins = ch->instr_ptr; + if (ins != NULL) { + if (ins->vol_env_flags & ENV_ENABLED) { + ch->vol_env_tick = 65535; + ch->vol_env_pos = 0; + } + + if (ins->pan_env_flags & ENV_ENABLED) { + ch->pan_env_tick = 65535; + ch->pan_env_pos = 0; + } + + ch->fadeout_speed = ins->fadeout; + ch->fadeout_vol = 32768; + + if (ins->auto_vib_depth > 0) { + ch->auto_vib_pos = 0; + + if (ins->auto_vib_sweep > 0) { + ch->auto_vib_amp = 0; + ch->auto_vib_sweep = (ins->auto_vib_depth << 8) / ins->auto_vib_sweep; + } else { + ch->auto_vib_amp = ins->auto_vib_depth << 8; + ch->auto_vib_sweep = 0; + } + } + } +} + +static void key_off(channel_t *ch) { + ch->key_off = true; + + instr_t *ins = ch->instr_ptr; + + if (ins->vol_env_flags & ENV_ENABLED) { + if (ch->vol_env_tick >= (uint16_t)ins->vol_env_points[ch->vol_env_pos][0]) { + ch->vol_env_tick = ins->vol_env_points[ch->vol_env_pos][0] - 1; + } + } else { + ch->real_vol = 0; + ch->out_vol = 0; + ch->status |= CS_UPDATE_VOL + CS_USE_QUICK_VOLRAMP; + } + + if (!(ins->pan_env_flags & ENV_ENABLED)) { + if (ch->pan_env_tick >= (uint16_t)ins->pan_env_points[ch->pan_env_pos][0]) { + ch->pan_env_tick = ins->pan_env_points[ch->pan_env_pos][0] - 1; + } + } +} + +static void trigger_note(uint8_t note, uint8_t efx, uint8_t efx_data, channel_t *ch) { + if (note == NOTE_OFF) { + key_off(ch); + return; + } + + if (note == 0) { + note = ch->note_num; + if (note == 0) { + return; + } + } + + ch->note_num = note; + instr_t *ins = instr[ch->instr_num]; + if (ins == NULL) { + ins = instr[0]; + } + + ch->instr_ptr = ins; + ch->mute = ins->mute; + + if (note > 96) { + note = 96; + } + + ch->smp_num = ins->note2_sample_lut[note-1] & 0xF; + sample_t *s = &ins->smp[ch->smp_num]; + + ch->smp_ptr = s; + ch->relative_note = s->relative_note; + + note += ch->relative_note; + if (note >= 10*12) { + return; + } + + ch->old_vol = s->volume; + ch->old_pan = s->panning; + + if (efx == 0xE && (efx_data & 0xF0) == 0x50) { + ch->finetune = ((efx_data & 0x0F) * 16) - 128; + } else { + ch->finetune = s->finetune; + } + + if (note != 0) { + const uint16_t note_index = ((note-1) * 16) + (((int8_t)ch->finetune >> 3) + 16); + ch->out_period = ch->real_period = note2_period_lut[note_index]; + } + + ch->status |= CF_UPDATE_PERIOD + CS_UPDATE_VOL + CS_UPDATE_PAN + CS_TRIGGER_VOICE + CS_USE_QUICK_VOLRAMP; + + if (efx == 9) { + if (efx_data > 0) { + ch->sample_offset = ch->efx_data; + } + + ch->smp_start_pos = ch->sample_offset << 8; + } else { + ch->smp_start_pos = 0; + } +} + +static void vol_slide(channel_t *ch, uint8_t param); +static void do_vibrato(channel_t *ch); +static void portamento(channel_t *ch, uint8_t param); + +static void dummy(channel_t *ch, uint8_t param) { + (void)ch; + (void)param; + return; +} + +static void fine_pitch_slide_up(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->f_pitch_slide_up_speed; + } + + ch->f_pitch_slide_up_speed = param; + + ch->real_period -= param * 4; + if ((int16_t)ch->real_period < 1) { + ch->real_period = 1; + } + + ch->out_period = ch->real_period; + ch->status |= CF_UPDATE_PERIOD; +} + +static void fine_pitch_slide_down(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->f_pitch_slide_down_speed; + } + + ch->f_pitch_slide_down_speed = param; + + ch->real_period += param * 4; + if ((int16_t)ch->real_period >= 32000) { + ch->real_period = 32000-1; + } + + ch->out_period = ch->real_period; + ch->status |= CF_UPDATE_PERIOD; +} + +static void set_portamento_ctrl(channel_t *ch, uint8_t param) { + ch->semitone_porta_mode = (param != 0); +} + +static void set_vibrato_ctrl(channel_t *ch, uint8_t param) { + ch->vib_trem_ctrl = (ch->vib_trem_ctrl & 0xF0) | param; +} + +static void pattern_loop(channel_t *ch, uint8_t param) { + if (param == 0) { + ch->pattern_loop_start_row = song.row & 0xFF; + } else if (ch->pattern_loop_counter == 0) { + ch->pattern_loop_counter = param; + + song.p_break_pos = ch->pattern_loop_start_row; + song.p_break_flag = true; + } else if (--ch->pattern_loop_counter > 0) { + song.p_break_pos = ch->pattern_loop_start_row; + song.p_break_flag = true; + } +} + +static void set_tremolo_ctrl(channel_t *ch, uint8_t param) { + ch->vib_trem_ctrl = (param << 4) | (ch->vib_trem_ctrl & 0x0F); +} + +static void fine_vol_slide_up(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->f_vol_slide_up_speed; + } + + ch->f_vol_slide_up_speed = param; + + ch->real_vol += param; + if (ch->real_vol > 64) { + ch->real_vol = 64; + } + + ch->out_vol = ch->real_vol; + ch->status |= CS_UPDATE_VOL; +} + +static void fine_vol_fine_down(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->f_vol_slide_down_speed; + } + + ch->f_vol_slide_down_speed = param; + + ch->real_vol -= param; + if ((int8_t)ch->real_vol < 0) { + ch->real_vol = 0; + } + + ch->out_vol = ch->real_vol; + ch->status |= CS_UPDATE_VOL; +} + +static void note_cut0(channel_t *ch, uint8_t param) { + if (param == 0) { + ch->real_vol = 0; + ch->out_vol = 0; + ch->status |= CS_UPDATE_VOL + CS_USE_QUICK_VOLRAMP; + } +} + +static void pattern_delay(channel_t *ch, uint8_t param) { + if (song.patt_del_time2 == 0) { + song.patt_del_time = param + 1; + } + + (void)ch; +} + +static const efx_routine e_jump_tab_tick_zero[16] = { + dummy, + fine_pitch_slide_up, + fine_pitch_slide_down, + set_portamento_ctrl, + set_vibrato_ctrl, + dummy, + pattern_loop, + set_tremolo_ctrl, + dummy, + dummy, + fine_vol_slide_up, + fine_vol_fine_down, + note_cut0, + dummy, + pattern_delay, + dummy +}; + +static void e_effects_tick_zero(channel_t *ch, uint8_t param) { + const uint8_t efx = param >> 4; + param &= 0x0F; + + if (ch->channel_off) { + if (efx == 0x6) { + pattern_loop(ch, param); + } else if (efx == 0xE) { + pattern_delay(ch, param); + } + + return; + } + + e_jump_tab_tick_zero[efx](ch, param); +} + +static void position_jump(channel_t *ch, uint8_t param) { + const int16_t pos = (int16_t)param - 1; + if (pos < 0 || pos >= song.song_length) { + bxx_overflow = true; + } else { + song.song_pos = pos; + } + + song.p_break_pos = 0; + song.pos_jump_flag = true; + + (void)ch; +} + +static void pattern_break(channel_t *ch, uint8_t param) { + param = ((param >> 4) * 10) + (param & 0x0F); + if (param <= 63) { + song.p_break_pos = param; + } else { + song.p_break_pos = 0; + } + + song.pos_jump_flag = true; + + (void)ch; +} + +static void set_speed(channel_t *ch, uint8_t param) { + if (param >= 32) { + song.bpm = param; + set_mixer_bpm(song.bpm); + } else { + song.tick = song.speed = param; + } + + (void)ch; +} + +static void set_global_volume(channel_t *ch, uint8_t param) { + if (param > 64) { + param = 64; + } + + song.global_volume = param; + + channel_t *c = channel; + for (int32_t i = 0; i < song.num_channels; i++, c++) { + c->status |= CS_UPDATE_VOL; + } + + (void)ch; +} + +static void set_envelope_pos(channel_t *ch, uint8_t param) { + bool env_update; + int8_t point; + int32_t tick; + + instr_t *ins = ch->instr_ptr; + + if (ins->vol_env_flags & ENV_ENABLED) { + ch->vol_env_tick = param - 1; + + point = 0; + env_update = true; + tick = param; + + if (ins->vol_env_length > 1) { + point++; + for (int32_t i = 0; i < ins->vol_env_length-1; i++) { + if (tick < ins->vol_env_points[point][0]) { + point--; + + tick -= ins->vol_env_points[point][0]; + if (tick == 0) { + env_update = false; + break; + } + + const int16_t x0 = ins->vol_env_points[point+0][0]; + const int16_t x1 = ins->vol_env_points[point+1][0]; + + const int16_t x_diff = x1 - x0; + if (x_diff <= 0) { + env_update = true; + break; + } + + const int16_t y0 = ins->vol_env_points[point+0][1]; + const int16_t y1 = ins->vol_env_points[point+1][1]; + + const int8_t y_diff = (int8_t)(y1 - y0); + ch->vol_env_delta = (y_diff * 256) / x_diff; + + ch->vol_env_value = (((int8_t)y0 * 256)) + (int16_t)(ch->vol_env_delta * (tick-1)); + + point++; + + env_update = false; + break; + } + + point++; + } + + if (env_update) { + point--; + } + } + + if (env_update) { + ch->vol_env_delta = 0; + ch->vol_env_value = ((int8_t)ins->vol_env_points[point][1] * 256); + } + + if (point >= ins->vol_env_length) { + point = ins->vol_env_length - 1; + if (point < 0) { + point = 0; + } + } + + ch->vol_env_pos = point; + } + + if (ins->vol_env_flags & ENV_SUSTAIN) { + ch->pan_env_tick = param - 1; + + point = 0; + env_update = true; + tick = param; + + if (ins->pan_env_length > 1) { + point++; + for (int32_t i = 0; i < ins->pan_env_length-1; i++) { + if (tick < ins->pan_env_points[point][0]) { + point--; + + tick -= ins->pan_env_points[point][0]; + if (tick == 0) { + env_update = false; + break; + } + + const int16_t x0 = ins->pan_env_points[point+0][0]; + const int16_t x1 = ins->pan_env_points[point+1][0]; + + const int16_t x_diff = x1 - x0; + if (x_diff <= 0) { + env_update = true; + break; + } + + const int16_t y0 = ins->pan_env_points[point+0][1]; + const int16_t y1 = ins->pan_env_points[point+1][1]; + + const int8_t y_diff = (int8_t)(y1 - y0); + ch->pan_env_delta = (y_diff * 256) / x_diff; + + ch->pan_env_value = (((int8_t)y0 * 256)) + (int16_t)(ch->pan_env_delta * (tick-1)); + + point++; + + env_update = false; + break; + } + + point++; + } + + if (env_update) { + point--; + } + } + + if (env_update) { + ch->pan_env_delta = 0; + ch->pan_env_value = ((int8_t)ins->pan_env_points[point][1] * 256); + } + + if (point >= ins->pan_env_length) { + point = ins->pan_env_length - 1; + if (point < 0) { + point = 0; + } + } + + ch->pan_env_pos = point; + } +} + +static const efx_routine jump_tab_tick_zero[36] = { + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + position_jump, + dummy, + pattern_break, + e_effects_tick_zero, + set_speed, + set_global_volume, + dummy, + dummy, + dummy, + dummy, + set_envelope_pos, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy +}; + +static void handle_more_effects_tick_zero(channel_t *ch) { + if (ch->efx > 35) { + return; + } + + jump_tab_tick_zero[ch->efx](ch, ch->efx_data); +} + +static void v_set_vib_speed(channel_t *ch, uint8_t *vol_column_data) { + *vol_column_data = (ch->vol_column_vol & 0x0F) * 4; + if (*vol_column_data != 0) { + ch->vibrato_speed = *vol_column_data; + } +} + +static void v_set_volume(channel_t *ch, uint8_t *vol_column_data) { + *vol_column_data -= 16; + if (*vol_column_data > 64) { + *vol_column_data = 64; + } + + ch->out_vol = ch->real_vol = *vol_column_data; + ch->status |= CS_UPDATE_VOL + CS_USE_QUICK_VOLRAMP; +} + +static void v_fine_vol_slide_down(channel_t *ch, uint8_t *vol_column_data) { + *vol_column_data = (uint8_t)(0 - (ch->vol_column_vol & 0x0F)) + ch->real_vol; + if ((int8_t)*vol_column_data < 0) { + *vol_column_data = 0; + } + + ch->out_vol = ch->real_vol = *vol_column_data; + ch->status |= CS_UPDATE_VOL; +} + +static void v_fine_vol_slide_up(channel_t *ch, uint8_t *vol_column_data) { + *vol_column_data = (ch->vol_column_vol & 0x0F) + ch->real_vol; + if (*vol_column_data > 64) { + *vol_column_data = 64; + } + + ch->out_vol = ch->real_vol = *vol_column_data; + ch->status |= CS_UPDATE_VOL; +} + +static void v_set_pan(channel_t *ch, uint8_t *vol_column_data) { + *vol_column_data <<= 4; + + ch->out_pan = *vol_column_data; + ch->status |= CS_UPDATE_PAN; +} + +static void v_vol_slide_down(channel_t *ch) { + uint8_t new_vol = (uint8_t)(0 - (ch->vol_column_vol & 0x0F)) + ch->real_vol; + if ((int8_t)new_vol < 0) { + new_vol = 0; + } + + ch->out_vol = ch->real_vol = new_vol; + ch->status |= CS_UPDATE_VOL; +} + +static void v_vol_slide_up(channel_t *ch) { + uint8_t new_vol = (ch->vol_column_vol & 0x0F) + ch->real_vol; + if (new_vol > 64) { + new_vol = 64; + } + + ch->out_vol = ch->real_vol = new_vol; + ch->status |= CS_UPDATE_VOL; +} + +static void v_vibrato(channel_t *ch) { + const uint8_t param = ch->vol_column_vol & 0xF; + if (param > 0) { + ch->vibrato_depth = param; + } + + do_vibrato(ch); +} + +static void v_pan_slide_left(channel_t *ch) { + uint16_t tmp16 = ch->out_pan + (uint8_t)(0 - (ch->vol_column_vol & 0x0F)); + if (tmp16 < 256) { + tmp16 = 0; + } + + ch->out_pan = (uint8_t)tmp16; + ch->status |= CS_UPDATE_PAN; +} + +static void v_pan_slide_right(channel_t *ch) { + uint16_t tmp16 = ch->out_pan + (ch->vol_column_vol & 0x0F); + if (tmp16 > 255) { + tmp16 = 255; + } + + ch->out_pan = (uint8_t)tmp16; + ch->status |= CS_UPDATE_PAN; +} + +static void v_portamento(channel_t *ch) { + portamento(ch, 0); +} + +static void v_dummy(channel_t *ch) { + (void)ch; + return; +} + +static void v_dummy2(channel_t *ch, uint8_t *vol_column_data) { + (void)ch; + (void)vol_column_data; + return; +} + +static const vol_column_efx_routine v_jump_tab_tick_non_zero[16] = { + v_dummy, + v_dummy, + v_dummy, + v_dummy, + v_dummy, + v_dummy, + v_vol_slide_down, + v_vol_slide_up, + v_dummy, + v_dummy, + v_dummy, + v_vibrato, + v_dummy, + v_pan_slide_left, + v_pan_slide_right, + v_portamento +}; + +static const vol_column_efx_routine2 v_jump_tab_tick_zero[16] = { + v_dummy2, + v_set_volume, + v_set_volume, + v_set_volume, + v_set_volume, + v_set_volume, + v_dummy2, + v_dummy2, + v_fine_vol_slide_down, + v_fine_vol_slide_up, + v_set_vib_speed, + v_dummy2, + v_set_pan, + v_dummy2, + v_dummy2, + v_dummy2 +}; + +static void set_pan(channel_t *ch, uint8_t param) { + ch->out_pan = param; + ch->status |= CS_UPDATE_PAN; +} + +static void set_vol(channel_t *ch, uint8_t param) { + if (param > 64) { + param = 64; + } + + ch->out_vol = ch->real_vol = param; + ch->status |= CS_UPDATE_VOL + CS_USE_QUICK_VOLRAMP; +} + +static void extra_fine_pitch_slide(channel_t *ch, uint8_t param) { + const uint8_t slide_type = param >> 4; + param &= 0x0F; + + if (slide_type == 1) { + if (param == 0) { + param = ch->ef_pitch_slide_up_speed; + } + + ch->ef_pitch_slide_up_speed = param; + + uint16_t new_period = ch->real_period; + + new_period -= param; + if ((int16_t)new_period < 1) { + new_period = 1; + } + + ch->out_period = ch->real_period = new_period; + ch->status |= CF_UPDATE_PERIOD; + } else if (slide_type == 2) { + if (param == 0) { + param = ch->ef_pitch_slide_down_speed; + } + + ch->ef_pitch_slide_down_speed = param; + + uint16_t new_period = ch->real_period; + + new_period += param; + if ((int16_t)new_period >= 32000) { + new_period = 32000-1; + } + + ch->out_period = ch->real_period = new_period; + ch->status |= CF_UPDATE_PERIOD; + } +} + +static void do_multi_note_retrig(channel_t *ch, uint8_t param) { + uint8_t cnt = ch->note_retrig_counter + 1; + if (cnt < ch->note_retrig_speed) { + ch->note_retrig_counter = cnt; + return; + } + + ch->note_retrig_counter = 0; + + int16_t vol = ch->real_vol; + switch (ch->note_retrig_vol) { + case 0x1: { + vol -= 1; + break; + } + case 0x2: { + vol -= 2; + break; + } + case 0x3: { + vol -= 4; + break; + } + case 0x4: { + vol -= 8; + break; + } + case 0x5: { + vol -= 16; + break; + } + case 0x6: { + vol = (vol >> 1) + (vol >> 3) + (vol >> 4); + break; + } + case 0x7: { + vol >>= 1; + break; + } + case 0x8: { + break; + } + case 0x9: { + vol += 1; + break; + } + case 0xA: { + vol += 2; + break; + } + case 0xB: { + vol += 4; + break; + } + case 0xC: { + vol += 8; + break; + } + case 0xD: { + vol += 16; + break; + } + case 0xE: { + vol = (vol >> 1) + vol; + break; + } + case 0xF: { + vol += vol; + break; + } + default: { + break; + } + } + vol = CLAMP(vol, 0, 64); + + ch->real_vol = (uint8_t)vol; + ch->out_vol = ch->real_vol; + + if (ch->vol_column_vol >= 0x10 && ch->vol_column_vol <= 0x50) { + ch->out_vol = ch->vol_column_vol - 0x10; + ch->real_vol = ch->out_vol; + } else if (ch->vol_column_vol >= 0xC0 && ch->vol_column_vol <= 0xCF) { + ch->out_pan = (ch->vol_column_vol & 0x0F) << 4; + } + + trigger_note(0, 0, 0, ch); + + (void)param; +} + +static void multi_note_retrig(channel_t *ch, uint8_t param, uint8_t volume_column_data) { + uint8_t tmp_param; + + tmp_param = param & 0x0F; + if (tmp_param == 0) { + tmp_param = ch->note_retrig_speed; + } + + ch->note_retrig_speed = tmp_param; + + tmp_param = param >> 4; + if (tmp_param == 0) { + tmp_param = ch->note_retrig_vol; + } + + ch->note_retrig_vol = tmp_param; + + if (volume_column_data == 0) { + do_multi_note_retrig(ch, 0); + } +} + +static void handle_effects_tick_zero(channel_t *ch) { + uint8_t new_vol_col = ch->vol_column_vol; + + v_jump_tab_tick_zero[ch->vol_column_vol >> 4](ch, &new_vol_col); + + const uint8_t param = ch->efx_data; + if (ch->efx == 0 && param == 0) { + return; + } + + if (ch->efx == 8) { + set_pan(ch, param); + } else if (ch->efx == 12) { + set_vol(ch, param); + } else if (ch->efx == 27) { + multi_note_retrig(ch, param, new_vol_col); + } else if (ch->efx == 33) { + extra_fine_pitch_slide(ch, param); + } + + handle_more_effects_tick_zero(ch); +} + +static void prepare_portamento(channel_t *ch, const note_t *p, uint8_t inst) { + if (p->note > 0) { + if (p->note == NOTE_OFF) { + key_off(ch); + } else { + const uint16_t note = (((p->note-1) + ch->relative_note) * 16) + (((int8_t)ch->finetune >> 3) + 16); + if (note < MAX_NOTES) { + ch->portamento_target_period = note2_period_lut[note]; + + if (ch->portamento_target_period == ch->real_period) { + ch->portamento_direction = 0; + } else if (ch->portamento_target_period > ch->real_period) { + ch->portamento_direction = 1; + } else { + ch->portamento_direction = 2; + } + } + } + } + + if (inst > 0) { + reset_volumes(ch); + if (p->note != NOTE_OFF) { + trigger_instrument(ch); + } + } +} + +static void get_new_note(channel_t *ch, const note_t *p) { + ch->vol_column_vol = p->vol; + + if (ch->efx == 0) { + if (ch->efx_data > 0) { + ch->out_period = ch->real_period; + ch->status |= CF_UPDATE_PERIOD; + } + } else { + if ((ch->efx == 4 || ch->efx == 6) && (p->efx != 4 && p->efx != 6)) { + ch->out_period = ch->real_period; + ch->status |= CF_UPDATE_PERIOD; + } + } + + ch->efx = p->efx; + ch->efx_data = p->efx_data; + ch->copy_of_instr_and_note = (p->instr << 8) | p->note; + + if (ch->channel_off) { + handle_more_effects_tick_zero(ch); + return; + } + + uint8_t inst = p->instr; + if (inst > 0) { + if (inst <= MAX_INST) { + ch->instr_num = inst; + } else { + inst = 0; + } + } + + if (p->efx == 0x0E && p->efx_data >= 0xD1 && p->efx_data <= 0xDF) { + return; + } + + if (p->efx != 0x0E || p->efx_data != 0x90) { + if ((ch->vol_column_vol & 0xF0) == 0xF0) { + const uint8_t param = ch->vol_column_vol & 0x0F; + if (param > 0) { + ch->portamento_speed = (param << 4) * 4; + } + + prepare_portamento(ch, p, inst); + handle_effects_tick_zero(ch); + + return; + } + + if (p->efx == 3 || p->efx == 5) { + if (p->efx != 5 && p->efx_data != 0) { + ch->portamento_speed = p->efx_data * 4; + } + + prepare_portamento(ch, p, inst); + handle_effects_tick_zero(ch); + + return; + } + + if (p->efx == 0x14 && p->efx_data == 0) { + key_off(ch); + + if (inst) { + reset_volumes(ch); + } + + handle_effects_tick_zero(ch); + + return; + } + + if (p->note == 0) { + if (inst > 0) { + reset_volumes(ch); + trigger_instrument(ch); + } + + handle_effects_tick_zero(ch); + + return; + } + } + + if (p->note == NOTE_OFF) { + key_off(ch); + } else { + trigger_note(p->note, p->efx, p->efx_data, ch); + } + + if (inst > 0) { + reset_volumes(ch); + if (p->note != NOTE_OFF) { + trigger_instrument(ch); + } + } + + handle_effects_tick_zero(ch); +} + +static void update_vol_pan_auto_vib(channel_t *ch) { + bool env_interpolate_flag, env_did_interpolate; + uint8_t env_pos; + int16_t env_val; + uint32_t f_vol; + + instr_t *ins = ch->instr_ptr; + + if (ch->key_off) { + if (ch->fadeout_speed > ch->fadeout_vol) { + ch->fadeout_vol = 0; + ch->fadeout_speed = 0; + } else { + ch->fadeout_vol -= ch->fadeout_speed; + } + + ch->status |= CS_UPDATE_VOL; + } + + if (!ch->mute) { + env_val = 0; + if (ins->vol_env_flags & ENV_ENABLED) { + env_did_interpolate = false; + env_pos = ch->vol_env_pos; + + ch->vol_env_tick++; + + if (ch->vol_env_tick == ins->vol_env_points[env_pos][0]) { + ch->vol_env_value = ((int8_t)ins->vol_env_points[env_pos][1] * 256); + + env_pos++; + if (ins->vol_env_flags & ENV_LOOP) { + env_pos--; + + if (env_pos == ins->vol_env_loop_end) { + if (!(ins->vol_env_flags & ENV_SUSTAIN) || env_pos != ins->vol_env_sustain || !ch->key_off) { + env_pos = ins->vol_env_loop_start; + ch->vol_env_tick = ins->vol_env_points[env_pos][0]; + ch->vol_env_value = ((int8_t)ins->vol_env_points[env_pos][1] * 256); + } + } + + env_pos++; + } + + if (env_pos < ins->vol_env_length) { + env_interpolate_flag = true; + if ((ins->vol_env_flags & ENV_SUSTAIN) && !ch->key_off) { + if (env_pos-1 == ins->vol_env_sustain) { + env_pos--; + ch->vol_env_delta = 0; + env_interpolate_flag = false; + } + } + + if (env_interpolate_flag) { + ch->vol_env_pos = env_pos; + + const int16_t x0 = ins->vol_env_points[env_pos-1][0]; + const int16_t x1 = ins->vol_env_points[env_pos-0][0]; + + const int16_t x_diff = x1 - x0; + if (x_diff > 0) { + const int16_t y0 = ins->vol_env_points[env_pos-1][1]; + const int16_t y1 = ins->vol_env_points[env_pos-0][1]; + + const int8_t y_diff = (int8_t)(y1 - y0); + ch->vol_env_delta = (y_diff * 256) / x_diff; + + env_val = ch->vol_env_value; + env_did_interpolate = true; + } else { + ch->vol_env_delta = 0; + } + } + } else { + ch->vol_env_delta = 0; + } + } + + if (!env_did_interpolate) { + ch->vol_env_value += ch->vol_env_delta; + env_val = ch->vol_env_value; + + const uint8_t env_hi_byte = (uint8_t)(env_val >> 8); + if (env_hi_byte > 64) { + if (env_hi_byte <= 160) { + env_val = 64*256; + } else { + env_val = 0; + } + + ch->vol_env_delta = 0; + } + } + + uint32_t env_scaled = (uint16_t)env_val; + if (env_scaled > 16384) { + env_scaled = 16384; + } + + const uint32_t vol = song.global_volume * ch->out_vol * ch->fadeout_vol; + f_vol = ((vol >> 12) * env_scaled) >> 15; + + ch->status |= CS_UPDATE_VOL; + } else { + const uint32_t vol = song.global_volume * ch->out_vol * ch->fadeout_vol; + f_vol = vol >> 13; + } + + if (f_vol > 16384) { + f_vol = 16384; + } + + ch->final_vol = (uint16_t)f_vol; + } else { + ch->final_vol = 0; + } + + env_val = 0; + if (ins->pan_env_flags & ENV_ENABLED) { + env_did_interpolate = false; + env_pos = ch->pan_env_pos; + + ch->pan_env_tick++; + + if (ch->pan_env_tick == ins->pan_env_points[env_pos][0]) { + ch->pan_env_value = ((int8_t)ins->pan_env_points[env_pos][1] * 256); + + env_pos++; + if (ins->pan_env_flags & ENV_LOOP) { + env_pos--; + + if (env_pos == ins->pan_env_loop_end) { + if (!(ins->pan_env_flags & ENV_SUSTAIN) || env_pos != ins->pan_env_sustain || !ch->key_off) { + env_pos = ins->pan_env_loop_start; + + ch->pan_env_tick = ins->pan_env_points[env_pos][0]; + ch->pan_env_value = ((int8_t)ins->pan_env_points[env_pos][1] * 256); + } + } + + env_pos++; + } + + if (env_pos < ins->pan_env_length) { + env_interpolate_flag = true; + if ((ins->pan_env_flags & ENV_SUSTAIN) && !ch->key_off) { + if (env_pos-1 == ins->pan_env_sustain) { + env_pos--; + ch->pan_env_delta = 0; + env_interpolate_flag = false; + } + } + + if (env_interpolate_flag) { + ch->pan_env_pos = env_pos; + + const int16_t x0 = ins->pan_env_points[env_pos-1][0]; + const int16_t x1 = ins->pan_env_points[env_pos-0][0]; + + const int16_t x_diff = x1 - x0; + if (x_diff > 0) { + const int16_t y0 = ins->pan_env_points[env_pos-1][1]; + const int16_t y1 = ins->pan_env_points[env_pos-0][1]; + + const int8_t y_diff = (int8_t)(y1 - y0); + ch->pan_env_delta = (y_diff * 256) / x_diff; + + env_val = ch->pan_env_value; + env_did_interpolate = true; + } else { + ch->pan_env_delta = 0; + } + } + } else { + ch->pan_env_delta = 0; + } + } + + if (!env_did_interpolate) { + ch->pan_env_value += ch->pan_env_delta; + env_val = ch->pan_env_value; + + const uint8_t env_hi_byte = (uint8_t)(env_val >> 8); + if (env_hi_byte > 64) { + if (env_hi_byte <= 160) { + env_val = 64*256; + } else { + env_val = 0; + } + + ch->pan_env_delta = 0; + } + } + + int16_t pan_mul = ch->out_pan - 128; + if (pan_mul >= 0) { + pan_mul = 0 - pan_mul; + } + pan_mul += 128; + pan_mul <<= 3; + + env_val -= 32*256; + const int8_t pan_add = (int8_t)((env_val * pan_mul) >> 16); + + ch->final_pan = (uint8_t)(ch->out_pan + pan_add); + ch->status |= CS_UPDATE_PAN; + } else { + ch->final_pan = ch->out_pan; + } + + if (ins->auto_vib_depth > 0) { + uint16_t auto_vib_amp; + + if (ch->auto_vib_sweep > 0) { + auto_vib_amp = ch->auto_vib_sweep; + if (!ch->key_off) { + auto_vib_amp += ch->auto_vib_amp; + if ((auto_vib_amp >> 8) > ins->auto_vib_depth) { + auto_vib_amp = ins->auto_vib_depth << 8; + ch->auto_vib_sweep = 0; + } + + ch->auto_vib_amp = auto_vib_amp; + } + } else { + auto_vib_amp = ch->auto_vib_amp; + } + + ch->auto_vib_pos += ins->auto_vib_rate; + + int16_t auto_vib_val; + if (ins->auto_vib_type == 1) { + auto_vib_val = (ch->auto_vib_pos > 127) ? 64 : -64; + } else if (ins->auto_vib_type == 2) { + auto_vib_val = (((ch->auto_vib_pos >> 1) + 64) & 127) - 64; + } else if (ins->auto_vib_type == 3) { + auto_vib_val = ((-(ch->auto_vib_pos >> 1) + 64) & 127) - 64; + } else { + auto_vib_val = auto_vib_sine_tab[ch->auto_vib_pos]; + } + + auto_vib_val = (auto_vib_val * (int16_t)auto_vib_amp) >> (6+8); + + uint16_t tmp_period = ch->out_period + auto_vib_val; + if (tmp_period >= 32000) { + tmp_period = 0; + } + + ch->final_period = tmp_period; + ch->status |= CF_UPDATE_PERIOD; + } else { + ch->final_period = ch->out_period; + } +} + +static uint16_t period2_note_period(uint16_t period, uint8_t output_note_offset, channel_t *ch) { + int32_t tmp_period; + + const int32_t fine_tune = ((int8_t)ch->finetune >> 3) + 16; + + int32_t hi_period = 8*12*16; + + int32_t lo_period = 0; + + for (int32_t i = 0; i < 8; i++) { + tmp_period = (((lo_period + hi_period) >> 1) & ~15) + fine_tune; + + int32_t look_up = tmp_period - 8; + if (look_up < 0) { + look_up = 0; + } + + if (period >= note2_period_lut[look_up]) { + hi_period = (tmp_period - fine_tune) & ~15; + } else { + lo_period = (tmp_period - fine_tune) & ~15; + } + } + + tmp_period = lo_period + fine_tune + (output_note_offset << 4); + if (tmp_period >= (8*12*16+15)-1) { + tmp_period = (8*12*16+16)-1; + } + + return note2_period_lut[tmp_period]; +} + +static void do_vibrato(channel_t *ch) { + uint8_t tmp_vib = (ch->vibrato_pos >> 2) & 0x1F; + + switch (ch->vib_trem_ctrl & 3) { + case 0: { + tmp_vib = vibrato_tab[tmp_vib]; + break; + } + + case 1: { + tmp_vib <<= 3; + if ((int8_t)ch->vibrato_pos < 0) { + tmp_vib = ~tmp_vib; + } + } + break; + + default: { + tmp_vib = 255; + break; + } + } + + tmp_vib = (tmp_vib * ch->vibrato_depth) >> 5; + + if ((int8_t)ch->vibrato_pos < 0) { + ch->out_period = ch->real_period - tmp_vib; + } else { + ch->out_period = ch->real_period + tmp_vib; + } + + ch->status |= CF_UPDATE_PERIOD; + ch->vibrato_pos += ch->vibrato_speed; +} + +static void arpeggio(channel_t *ch, uint8_t param) { + uint8_t note_offset; + + const uint8_t tick = arpeggio_tab[song.tick & 31]; + if (tick == 0) { + ch->out_period = ch->real_period; + } else { + if (tick == 1) { + note_offset = param >> 4; + } else { + note_offset = param & 0x0F; + } + + ch->out_period = period2_note_period(ch->real_period, note_offset, ch); + } + + ch->status |= CF_UPDATE_PERIOD; +} + +static void pitch_slide_up(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->pitch_slide_up_speed; + } + + ch->pitch_slide_up_speed = param; + + ch->real_period -= param * 4; + if ((int16_t)ch->real_period < 1) { + ch->real_period = 1; + } + + ch->out_period = ch->real_period; + ch->status |= CF_UPDATE_PERIOD; +} + +static void pitch_slide_down(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->pitch_slide_down_speed; + } + + ch->pitch_slide_down_speed = param; + + ch->real_period += param * 4; + if ((int16_t)ch->real_period >= 32000) { + ch->real_period = 32000-1; + } + + ch->out_period = ch->real_period; + ch->status |= CF_UPDATE_PERIOD; +} + +static void portamento(channel_t *ch, uint8_t param) { + if (ch->portamento_direction == 0) { + return; + } + + if (ch->portamento_direction > 1) { + ch->real_period -= ch->portamento_speed; + if ((int16_t)ch->real_period <= (int16_t)ch->portamento_target_period) { + ch->portamento_direction = 1; + ch->real_period = ch->portamento_target_period; + } + } else { + ch->real_period += ch->portamento_speed; + if (ch->real_period >= ch->portamento_target_period) { + ch->portamento_direction = 1; + ch->real_period = ch->portamento_target_period; + } + } + + if (ch->semitone_porta_mode) { + ch->out_period = period2_note_period(ch->real_period, 0, ch); + } else { + ch->out_period = ch->real_period; + } + + ch->status |= CF_UPDATE_PERIOD; + + (void)param; +} + +static void vibrato(channel_t *ch, uint8_t param) { + if (param > 0) { + const uint8_t vibrato_depth = param & 0x0F; + if (vibrato_depth > 0) { + ch->vibrato_depth = vibrato_depth; + } + + const uint8_t vibrato_speed = (param & 0xF0) >> 2; + if (vibrato_speed > 0) { + ch->vibrato_speed = vibrato_speed; + } + } + + do_vibrato(ch); +} + +static void portamento_plus_vol_slide(channel_t *ch, uint8_t param) { + portamento(ch, 0); + vol_slide(ch, param); + + (void)param; +} + +static void vibrato_plus_vol_slide(channel_t *ch, uint8_t param) { + do_vibrato(ch); + vol_slide(ch, param); + + (void)param; +} + +static void tremolo(channel_t *ch, uint8_t param) { + int16_t trem_vol; + + if (param > 0) { + uint8_t tremolo_depth = param & 0x0F; + if (tremolo_depth > 0) { + ch->tremolo_depth = tremolo_depth; + } + + uint8_t tremolo_speed = (param & 0xF0) >> 2; + if (tremolo_speed > 0) { + ch->tremolo_speed = tremolo_speed; + } + } + + uint8_t tmp_trem = (ch->tremolo_pos >> 2) & 0x1F; + switch ((ch->vib_trem_ctrl >> 4) & 3) { + case 0: { + tmp_trem = vibrato_tab[tmp_trem]; + break; + } + case 1: { + tmp_trem <<= 3; + if ((int8_t)ch->vibrato_pos < 0) { + tmp_trem = ~tmp_trem; + } + break; + } + default: { + tmp_trem = 255; + break; + } + } + tmp_trem = (tmp_trem * ch->tremolo_depth) >> 6; + + if ((int8_t)ch->tremolo_pos < 0) { + trem_vol = ch->real_vol - tmp_trem; + if (trem_vol < 0) { + trem_vol = 0; + } + } else { + trem_vol = ch->real_vol + tmp_trem; + if (trem_vol > 64) { + trem_vol = 64; + } + } + + ch->out_vol = (uint8_t)trem_vol; + ch->status |= CS_UPDATE_VOL; + + ch->tremolo_pos += ch->tremolo_speed; +} + +static void vol_slide(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->vol_slide_speed; + } + + ch->vol_slide_speed = param; + + uint8_t new_vol = ch->real_vol; + if ((param & 0xF0) == 0) { + new_vol -= param; + if ((int8_t)new_vol < 0) { + new_vol = 0; + } + } else { + param >>= 4; + + new_vol += param; + if (new_vol > 64) { + new_vol = 64; + } + } + + ch->out_vol = ch->real_vol = new_vol; + ch->status |= CS_UPDATE_VOL; +} + +static void global_vol_slide(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->glob_vol_slide_speed; + } + + ch->glob_vol_slide_speed = param; + + uint8_t new_vol = (uint8_t)song.global_volume; + if ((param & 0xF0) == 0) { + new_vol -= param; + if ((int8_t)new_vol < 0) { + new_vol = 0; + } + } else { + param >>= 4; + + new_vol += param; + if (new_vol > 64) { + new_vol = 64; + } + } + + song.global_volume = new_vol; + + channel_t *c = channel; + for (int32_t i = 0; i < song.num_channels; i++, c++) { + c->status |= CS_UPDATE_VOL; + } +} + +static void key_off_cmd(channel_t *ch, uint8_t param) { + if ((uint8_t)(song.speed-song.tick) == (param & 31)) { + key_off(ch); + } +} + +static void panning_slide(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->panning_slide_speed; + } + + ch->panning_slide_speed = param; + + int16_t new_pan = (int16_t)ch->out_pan; + if ((param & 0xF0) == 0) { + new_pan -= param; + if (new_pan < 0) { + new_pan = 0; + } + } else { + param >>= 4; + + new_pan += param; + if (new_pan > 255) { + new_pan = 255; + } + } + + ch->out_pan = (uint8_t)new_pan; + ch->status |= CS_UPDATE_PAN; +} + +static void tremor(channel_t *ch, uint8_t param) { + if (param == 0) { + param = ch->tremor_param; + } + + ch->tremor_param = param; + + uint8_t tremor_sign = ch->tremor_pos & 0x80; + uint8_t tremor_data = ch->tremor_pos & 0x7F; + + tremor_data--; + if ((int8_t)tremor_data < 0) { + if (tremor_sign == 0x80) { + tremor_sign = 0x00; + tremor_data = param & 0x0F; + } else { + tremor_sign = 0x80; + tremor_data = param >> 4; + } + } + + ch->tremor_pos = tremor_sign | tremor_data; + ch->out_vol = (tremor_sign == 0x80) ? ch->real_vol : 0; + ch->status |= CS_UPDATE_VOL + CS_USE_QUICK_VOLRAMP; +} + +static void retrig_note(channel_t *ch, uint8_t param) { + if (param == 0) { + return; + } + + if ((song.speed-song.tick) % param == 0) { + trigger_note(0, 0, 0, ch); + trigger_instrument(ch); + } +} + +static void note_cut(channel_t *ch, uint8_t param) { + if ((uint8_t)(song.speed-song.tick) == param) { + ch->out_vol = ch->real_vol = 0; + ch->status |= CS_UPDATE_VOL + CS_USE_QUICK_VOLRAMP; + } +} + +static void note_delay(channel_t *ch, uint8_t param) { + if ((uint8_t)(song.speed-song.tick) == param) { + const uint8_t note = ch->copy_of_instr_and_note & 0x00FF; + trigger_note(note, 0, 0, ch); + + const uint8_t instrument = ch->copy_of_instr_and_note >> 8; + if (instrument > 0) { + reset_volumes(ch); + } + + trigger_instrument(ch); + + if (ch->vol_column_vol >= 0x10 && ch->vol_column_vol <= 0x50) { + ch->out_vol = ch->vol_column_vol - 16; + ch->real_vol = ch->out_vol; + } else if (ch->vol_column_vol >= 0xC0 && ch->vol_column_vol <= 0xCF) { + ch->out_pan = (ch->vol_column_vol & 0x0F) << 4; + } + } +} + +static const efx_routine e_jump_tab_tick_non_zero[16] = { + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy, + retrig_note, + dummy, + dummy, + note_cut, + note_delay, + dummy, + dummy +}; + +static void e_effects_tick_non_zero(channel_t *ch, uint8_t param) { + e_jump_tab_tick_non_zero[param >> 4](ch, param & 0xF); +} + +static const efx_routine jump_tab_tick_non_zero[36] = { + arpeggio, + pitch_slide_up, + pitch_slide_down, + portamento, + vibrato, + portamento_plus_vol_slide, + vibrato_plus_vol_slide, + tremolo, + dummy, + dummy, + vol_slide, + dummy, + dummy, + dummy, + e_effects_tick_non_zero, + dummy, + dummy, + global_vol_slide, + dummy, + dummy, + key_off_cmd, + dummy, + dummy, + dummy, + dummy, + panning_slide, + dummy, + do_multi_note_retrig, + dummy, + tremor, + dummy, + dummy, + dummy, + dummy, + dummy, + dummy +}; + +static void handle_effects_tick_non_zero(channel_t *ch) { + if (ch->channel_off) { + return; + } + + v_jump_tab_tick_non_zero[ch->vol_column_vol >> 4](ch); + + if ((ch->efx == 0 && ch->efx_data == 0) || ch->efx > 35) { + return; + } + + jump_tab_tick_non_zero[ch->efx](ch, ch->efx_data); +} + +static void get_next_pos(void) { + if (song.tick != 1) { + return; + } + + song.row++; + + if (song.patt_del_time > 0) { + song.patt_del_time2 = song.patt_del_time; + song.patt_del_time = 0; + } + + if (song.patt_del_time2 > 0) { + song.patt_del_time2--; + if (song.patt_del_time2 > 0) { + song.row--; + } + } + + if (song.p_break_flag) { + song.p_break_flag = false; + song.row = song.p_break_pos; + } + + if (song.row >= song.curr_num_rows || song.pos_jump_flag) { + song.row = song.p_break_pos; + song.p_break_pos = 0; + song.pos_jump_flag = false; + + if (bxx_overflow) { + song.song_pos = 0; + bxx_overflow = false; + } else if (++song.song_pos >= song.song_length) { + song.song_pos = song.song_loop_start; + } + + song.patt_num = song.orders[song.song_pos & 0xFF]; + song.curr_num_rows = pattern_num_rows[song.patt_num & 0xFF]; + + if (song.row >= song.curr_num_rows) { + song.row = 0; + } + } +} + +static void tick_replayer(void) { + channel_t *ch; + + bool tick_zero = false; + if (--song.tick == 0) { + song.tick = song.speed; + tick_zero = true; + } + + const bool read_new_note = tick_zero && (song.patt_del_time2 == 0); + if (read_new_note) { + const note_t *p = nil_pattern_line; + if (pattern[song.patt_num] != NULL) { + p = &pattern[song.patt_num][song.row * song.num_channels]; + } + + ch = channel; + for (int32_t i = 0; i < song.num_channels; i++, ch++, p++) { + get_new_note(ch, p); + update_vol_pan_auto_vib(ch); + } + } else { + ch = channel; + for (int32_t i = 0; i < song.num_channels; i++, ch++) { + handle_effects_tick_non_zero(ch); + update_vol_pan_auto_vib(ch); + } + } + + get_next_pos(); +} + +static uint32_t period2_voice_delta(uint32_t period) { + uint32_t delta; + + period &= 0xFFFF; + + if (period == 0) { + return 0; + } + + if (linear_periods_flag) { + const uint32_t inv_period = ((12 * 192 * 4) - period) & 0xFFFF; + + const uint32_t quotient = inv_period / (12 * 16 * 4); + const uint32_t remainder = inv_period % (12 * 16 * 4); + const uint8_t shift_value = (14 - quotient) & 31; + + delta = (uint32_t)(((uint64_t)log_tab[remainder] * FREQUENCY_MUL_FACTOR) >> 24); + delta >>= shift_value; + } else { + delta = FREQUENCY_DIV_FACTOR / period; + } + + if (delta > MAX_VOICE_DELTA) { + delta = MAX_VOICE_DELTA; + } + + return delta; +} + +static uint32_t integer_sqrt(uint64_t n) { + uint64_t result = 0; + uint64_t bit = (uint64_t)1 << 62; + + while (bit > n) { + bit >>= 2; + } + + while (bit != 0) { + if (n >= result + bit) { + n -= result + bit; + result = (result >> 1) + bit; + } else { + result >>= 1; + } + + bit >>= 2; + } + + return (uint32_t)result; +} + +static void build_panning_table(void) { + if (panning_table_ready) { + return; + } + + for (uint32_t i = 0; i <= 256; i++) { + panning_table[i] = (integer_sqrt((uint64_t)i << 26) + 1) >> 1; + } + + panning_table_ready = true; +} + +static void clear_voice(voice_t *v) { + *v = (voice_t){ 0 }; + v->panning = 128; +} + +static void stop_voice(int32_t i) { + clear_voice(&voice[i]); + clear_voice(&voice[MAX_CHANNELS + i]); +} + +static void reset_channels(void) { + for (int32_t i = 0; i < MAX_CHANNELS; i++) { + channel_t *ch = &channel[i]; + + *ch = (channel_t){ 0 }; + ch->instr_ptr = instr[0]; + ch->status = CS_UPDATE_VOL; + ch->old_pan = 128; + ch->out_pan = 128; + ch->final_pan = 128; + } +} + +static void stop_voices(void) { + channel_t *ch = channel; + for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) { + ch->copy_of_instr_and_note = 0; + ch->relative_note = 0; + ch->smp_num = 0; + ch->smp_ptr = NULL; + ch->instr_num = 0; + ch->instr_ptr = instr[0]; + ch->status = CS_UPDATE_VOL; + ch->real_vol = ch->out_vol = ch->old_vol = 0; + ch->final_vol = 0; + ch->old_pan = ch->out_pan = ch->final_pan = 128; + ch->vibrato_depth = 0; + ch->portamento_direction = 0; + + stop_voice(i); + } +} + +static void reset_replayer_state(void) { + song.patt_del_time = song.patt_del_time2 = 0; + song.pos_jump_flag = false; + song.p_break_pos = 0; + song.p_break_flag = false; + song.global_volume = 64; + + channel_t *ch = channel; + for (int32_t i = 0; i < song.num_channels; i++, ch++) { + ch->pattern_loop_start_row = 0; + ch->pattern_loop_counter = 0; + ch->status |= CS_UPDATE_VOL; + } +} + +static void set_song_pos(int16_t song_pos, int16_t row) { + song.song_pos = song_pos; + if (song.song_length > 0 && song.song_pos >= song.song_length) { + song.song_pos = song.song_length - 1; + } + + song.patt_num = song.orders[song.song_pos]; + song.curr_num_rows = pattern_num_rows[song.patt_num]; + + song.row = row; + if (song.row >= song.curr_num_rows) { + song.row = song.curr_num_rows - 1; + } + + song.tick = 1; +} + +static void voice_update_volumes(int32_t i, uint8_t status) { + voice_t *v = &voice[i]; + + v->target_volume_l = (int32_t)(((uint32_t)v->volume * panning_table[256 - v->panning]) >> 16) << 8; + v->target_volume_r = (int32_t)(((uint32_t)v->volume * panning_table[v->panning]) >> 16) << 8; + + const bool voice_trigger_flag = !!(status & CS_TRIGGER_VOICE); + if (voice_trigger_flag) { + if (v->curr_volume_l > 0 || v->curr_volume_r > 0) { + voice_t *f = &voice[MAX_CHANNELS + i]; + + *f = *v; + + f->target_volume_l = 0; + f->target_volume_r = 0; + f->volume_ramp_length = QUICK_VOLRAMP_SAMPLES; + f->volume_delta_l = (0 - f->curr_volume_l) / (int32_t)QUICK_VOLRAMP_SAMPLES; + f->volume_delta_r = (0 - f->curr_volume_r) / (int32_t)QUICK_VOLRAMP_SAMPLES; + f->is_fade_out_voice = true; + } + + v->curr_volume_l = v->curr_volume_r = 0; + } + + if (!voice_trigger_flag && v->target_volume_l == v->curr_volume_l && v->target_volume_r == v->curr_volume_r) { + v->volume_ramp_length = 0; + } else { + const int32_t volume_l_diff = v->target_volume_l - v->curr_volume_l; + const int32_t volume_r_diff = v->target_volume_r - v->curr_volume_r; + + if (status & CS_USE_QUICK_VOLRAMP) { + v->volume_ramp_length = QUICK_VOLRAMP_SAMPLES; + } else { + v->volume_ramp_length = samples_per_tick; + } + + v->volume_delta_l = volume_l_diff / (int32_t)v->volume_ramp_length; + v->volume_delta_r = volume_r_diff / (int32_t)v->volume_ramp_length; + } +} + +static void voice_trigger(int32_t ch, sample_t *s, int32_t position) { + voice_t *v = &voice[ch]; + + const int32_t length = s->length; + const int32_t loop_start = s->loop_start; + const int32_t loop_length = s->loop_length; + const int32_t loop_end = s->loop_start + s->loop_length; + uint8_t loop_type = GET_LOOPTYPE(s->flags); + + if (s->data_ptr == NULL || length < 1) { + v->active = false; + return; + } + + if (loop_length < 1) { + loop_type = LOOP_DISABLED; + } + + v->data = s->data_ptr; + v->is_16bit = !!(s->flags & SAMPLE_16BIT); + v->sampling_backwards = false; + v->loop_type = loop_type; + v->sample_end = (loop_type == LOOP_DISABLED) ? (uint32_t)length : (uint32_t)loop_end; + v->loop_start = (uint32_t)loop_start; + v->loop_length = (uint32_t)loop_length; + v->position = (uint32_t)position; + v->position_frac = 0; + v->is_fade_out_voice = false; + + if (v->position >= v->sample_end) { + v->active = false; + return; + } + + v->active = true; +} + +static void update_voices(void) { + channel_t *ch = channel; + voice_t *v = voice; + + for (int32_t i = 0; i < song.num_channels; i++, ch++, v++) { + const uint8_t status = ch->status; + if (status == 0) { + continue; + } + + ch->status = 0; + + if (status & CS_UPDATE_VOL) { + v->volume = ch->final_vol; + } + + if (status & CS_UPDATE_PAN) { + v->panning = ch->final_pan; + } + + if (status & (CS_UPDATE_VOL | CS_UPDATE_PAN)) { + voice_update_volumes(i, status); + } + + if (status & CF_UPDATE_PERIOD) { + v->delta = period2_voice_delta(ch->final_period); + } + + if (status & CS_TRIGGER_VOICE) { + voice_trigger(i, ch->smp_ptr, ch->smp_start_pos); + } + } +} + +static void reset_ramp_volumes(void) { + voice_t *v = voice; + for (int32_t i = 0; i < song.num_channels; i++, v++) { + v->curr_volume_l = v->target_volume_l; + v->curr_volume_r = v->target_volume_r; + v->volume_ramp_length = 0; + } +} + +static void voice_wrap(voice_t *v) { + if (v->loop_type == LOOP_DISABLED) { + v->active = false; + return; + } + + const uint32_t overflow = v->position - v->sample_end; + + if (v->loop_type == LOOP_FORWARD) { + if (v->loop_length >= 2) { + v->position = v->loop_start + ((overflow < v->loop_length) ? overflow : (overflow % v->loop_length)); + } else { + v->position = v->loop_start; + } + } else { + if (v->loop_length >= 2) { + const uint32_t cycles = (overflow < v->loop_length) ? 0 : (overflow / v->loop_length); + const uint32_t phase = (overflow < v->loop_length) ? overflow : (overflow % v->loop_length); + + v->position = v->loop_start + phase; + if (!(cycles & 1)) { + v->sampling_backwards = !v->sampling_backwards; + } + } else { + v->position = v->loop_start; + } + } +} + +static void advance_voice(voice_t *v, uint32_t count) { + const uint64_t advance = (uint64_t)v->delta * count + v->position_frac; + + v->position_frac = (uint32_t)(advance & 0xFFFF); + v->position += (uint32_t)(advance >> 16); + + if (v->position >= v->sample_end) { + voice_wrap(v); + } +} + +static void fetch_samples(const voice_t *v, int32_t *s0, int32_t *s1) { + uint32_t index0, index1; + bool has_next = true; + + if (!v->sampling_backwards) { + index0 = v->position; + index1 = index0 + 1; + + if (index1 >= v->sample_end) { + if (v->loop_type == LOOP_FORWARD) { + index1 = v->loop_start; + } else if (v->loop_type == LOOP_PINGPONG) { + index1 = index0; + } else { + has_next = false; + } + } + } else { + index0 = v->loop_start + v->loop_start + v->loop_length - 1 - v->position; + index1 = (index0 > v->loop_start) ? (index0 - 1) : index0; + } + + if (v->is_16bit) { + const int16_t *data16 = (const int16_t *)v->data; + + *s0 = data16[index0]; + *s1 = has_next ? data16[index1] : 0; + } else { + *s0 = (int32_t)v->data[index0] * 256; + *s1 = has_next ? ((int32_t)v->data[index1] * 256) : 0; + } +} + +static void mix_span(voice_t *v, int32_t *dl, int32_t *dr, uint32_t count, bool ramping) { + int32_t curr_l = v->curr_volume_l; + int32_t curr_r = v->curr_volume_r; + + for (uint32_t i = 0; i < count; i++) { + int32_t s0, s1; + fetch_samples(v, &s0, &s1); + + const int32_t sample = s0 + (((s1 - s0) * (int32_t)(v->position_frac >> 1)) >> 15); + + dl[i] += (sample * (curr_l >> 8)) >> 14; + dr[i] += (sample * (curr_r >> 8)) >> 14; + + if (ramping) { + curr_l += v->volume_delta_l; + curr_r += v->volume_delta_r; + } + + v->position_frac += v->delta; + v->position += v->position_frac >> 16; + v->position_frac &= 0xFFFF; + + if (v->position >= v->sample_end) { + voice_wrap(v); + if (!v->active) { + break; + } + } + } + + v->curr_volume_l = curr_l; + v->curr_volume_r = curr_r; +} + +static void mix_voice(voice_t *v, int32_t *dl, int32_t *dr, uint32_t total) { + while (total > 0 && v->active) { + uint32_t count = total; + const bool ramping = v->volume_ramp_length > 0; + + if (ramping && count > v->volume_ramp_length) { + count = v->volume_ramp_length; + } + + if (!ramping && v->curr_volume_l == 0 && v->curr_volume_r == 0) { + advance_voice(v, count); + } else { + mix_span(v, dl, dr, count, ramping); + } + + if (ramping) { + v->volume_ramp_length -= count; + if (v->volume_ramp_length == 0) { + v->curr_volume_l = v->target_volume_l; + v->curr_volume_r = v->target_volume_r; + + if (v->is_fade_out_voice) { + v->active = false; + } + } + } + + dl += count; + dr += count; + total -= count; + } +} + +static void render_chunk(uint32_t count) { + for (uint32_t i = 0; i < count; i++) { + mix_l[i] = mix_r[i] = 0; + } + + for (int32_t i = 0; i < song.num_channels; i++) { + if (voice[i].active) { + mix_voice(&voice[i], mix_l, mix_r, count); + } + + if (voice[MAX_CHANNELS + i].active) { + mix_voice(&voice[MAX_CHANNELS + i], mix_l, mix_r, count); + } + } +} + +static uint32_t next_random(void) { + dither_state ^= dither_state << 13; + dither_state ^= dither_state >> 17; + dither_state ^= dither_state << 5; + + return dither_state; +} + +static int8_t to_sample8(int32_t accumulated) { + const uint32_t random = next_random(); + const int32_t triangular = (int32_t)(random & 1023) + (int32_t)((random >> 10) & 1023) - 1023; + + int32_t out = (accumulated + (1 << (OUTPUT_SHIFT - 1)) + triangular) >> OUTPUT_SHIFT; + if (out > 127) { + out = 127; + } else if (out < -128) { + out = -128; + } + + return (int8_t)out; +} + +static void run_tick(void) { + reset_ramp_volumes(); + tick_replayer(); + + const uint32_t numerator = SAMPLES_PER_TICK_NUMERATOR + tick_remainder; + samples_per_tick = numerator / mixer_bpm; + tick_remainder = numerator % mixer_bpm; + + update_voices(); + + tick_samples_left = samples_per_tick; +} + +static void audio_source(void *user) { + (void)user; + + if (!song_playing || audio_left < 0) { + return; + } + + uint32_t space = ta_audio_free(audio_left); + if (audio_right >= 0) { + const uint32_t space_right = ta_audio_free(audio_right); + if (space_right < space) { + space = space_right; + } + } + + while (space > 0) { + if (tick_samples_left == 0) { + run_tick(); + } + + uint32_t count = space; + if (count > tick_samples_left) { + count = tick_samples_left; + } + if (count > MIX_CHUNK) { + count = MIX_CHUNK; + } + + render_chunk(count); + + if (audio_right >= 0) { + for (uint32_t i = 0; i < count; i++) { + out_l[i] = to_sample8(mix_l[i]); + out_r[i] = to_sample8(mix_r[i]); + } + + ta_audio_write(audio_left, out_l, count); + ta_audio_write(audio_right, out_r, count); + } else { + for (uint32_t i = 0; i < count; i++) { + out_l[i] = to_sample8((mix_l[i] + mix_r[i]) >> 1); + } + + ta_audio_write(audio_left, out_l, count); + } + + tick_samples_left -= count; + space -= count; + } +} + +typedef struct reader_t { + const uint8_t *data; + uint32_t size; + uint32_t pos; +} reader_t; + +static uint8_t read8(reader_t *r) { + if (r->pos >= r->size) { + return 0; + } + + return r->data[r->pos++]; +} + +static uint16_t read16(reader_t *r) { + const uint16_t low = read8(r); + const uint16_t high = read8(r); + + return (uint16_t)(low | (high << 8)); +} + +static void skip_bytes(reader_t *r, uint32_t count) { + if (count > r->size - r->pos) { + r->pos = r->size; + } else { + r->pos += count; + } +} + +static uint16_t le16(const uint8_t *p) { + return (uint16_t)(p[0] | (p[1] << 8)); +} + +static uint32_t le32(const uint8_t *p) { + return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); +} + +static uint32_t extra_sample_lengths[32 - MAX_SMP_PER_INST]; + +static instr_t *allocate_instr(int32_t ins_num) { + if (instr[ins_num] != NULL) { + return instr[ins_num]; + } + + instr_t *p = (instr_t *)malloc(sizeof (instr_t)); + if (p == NULL) { + return NULL; + } + + *p = (instr_t){ 0 }; + + for (int32_t i = 0; i < MAX_SMP_PER_INST; i++) { + p->smp[i].panning = 128; + p->smp[i].volume = 64; + } + + instr[ins_num] = p; + + return p; +} + +static void free_song(void) { + for (int32_t i = 0; i < MAX_PATTERNS; i++) { + free(pattern[i]); + pattern[i] = NULL; + pattern_num_rows[i] = 64; + } + + for (int32_t i = 0; i <= MAX_INST; i++) { + if (instr[i] != NULL) { + for (int32_t j = 0; j < MAX_SMP_PER_INST; j++) { + free(instr[i]->smp[j].data_ptr); + } + + free(instr[i]); + instr[i] = NULL; + } + } + + song_loaded = false; +} + +static void sanitize_instrument(instr_t *ins) { + if (ins->auto_vib_depth > 0x0F) { + ins->auto_vib_depth = 0x0F; + } + if (ins->auto_vib_rate > 0x3F) { + ins->auto_vib_rate = 0x3F; + } + if (ins->auto_vib_type > 3) { + ins->auto_vib_type = 0; + } + + for (int32_t i = 0; i < 96; i++) { + if (ins->note2_sample_lut[i] >= MAX_SMP_PER_INST) { + ins->note2_sample_lut[i] = MAX_SMP_PER_INST - 1; + } + } + + if (ins->vol_env_length > 12) { + ins->vol_env_length = 12; + } + if (ins->vol_env_loop_start > 11) { + ins->vol_env_loop_start = 11; + } + if (ins->vol_env_loop_end > 11) { + ins->vol_env_loop_end = 11; + } + if (ins->vol_env_sustain > 11) { + ins->vol_env_sustain = 11; + } + if (ins->pan_env_length > 12) { + ins->pan_env_length = 12; + } + if (ins->pan_env_loop_start > 11) { + ins->pan_env_loop_start = 11; + } + if (ins->pan_env_loop_end > 11) { + ins->pan_env_loop_end = 11; + } + if (ins->pan_env_sustain > 11) { + ins->pan_env_sustain = 11; + } + + for (int32_t i = 0; i < 12; i++) { + if ((uint16_t)ins->vol_env_points[i][0] > 32767) { + ins->vol_env_points[i][0] = 32767; + } + if ((uint16_t)ins->pan_env_points[i][0] > 32767) { + ins->pan_env_points[i][0] = 32767; + } + if ((uint16_t)ins->vol_env_points[i][1] > 64) { + ins->vol_env_points[i][1] = 64; + } + if ((uint16_t)ins->pan_env_points[i][1] > 63) { + ins->pan_env_points[i][1] = 63; + } + } +} + +static void sanitize_sample(sample_t *s) { + if (GET_LOOPTYPE(s->flags) == (LOOP_FORWARD | LOOP_PINGPONG)) { + s->flags &= ~LOOP_FORWARD; + } + + if (s->volume > 64) { + s->volume = 64; + } + + s->relative_note = CLAMP(s->relative_note, -48, 71); + s->length = CLAMP(s->length, 0, MAX_SAMPLE_LEN); + + if (s->loop_start < 0 || s->loop_length <= 0 || s->loop_start + s->loop_length > s->length) { + s->loop_start = 0; + s->loop_length = 0; + DISABLE_LOOP(s->flags); + } +} + +static bool load_instr_header(reader_t *r, int32_t ins_num) { + uint8_t ih[INSTR_HEADER_SIZE]; + + for (int32_t i = 0; i < INSTR_HEADER_SIZE; i++) { + ih[i] = 0; + } + + if (r->size - r->pos < 4) { + return false; + } + + const uint32_t instr_size = le32(&r->data[r->pos]); + + uint32_t read_size = instr_size; + if (read_size == 0 || read_size > INSTR_HEADER_SIZE) { + read_size = INSTR_HEADER_SIZE; + } + + for (uint32_t i = 0; i < read_size; i++) { + ih[i] = read8(r); + } + + if (instr_size > INSTR_HEADER_SIZE) { + skip_bytes(r, instr_size - INSTR_HEADER_SIZE); + } + + const int16_t num_samples = (int16_t)le16(&ih[27]); + if (num_samples < 0 || num_samples > 32) { + return false; + } + + for (int32_t i = 0; i < 32 - MAX_SMP_PER_INST; i++) { + extra_sample_lengths[i] = 0; + } + + if (num_samples == 0) { + return true; + } + + instr_t *ins = allocate_instr(ins_num); + if (ins == NULL) { + return false; + } + + for (int32_t i = 0; i < 96; i++) { + ins->note2_sample_lut[i] = ih[33 + i]; + } + + for (int32_t i = 0; i < 12; i++) { + ins->vol_env_points[i][0] = (int16_t)le16(&ih[129 + i * 4]); + ins->vol_env_points[i][1] = (int16_t)le16(&ih[129 + i * 4 + 2]); + ins->pan_env_points[i][0] = (int16_t)le16(&ih[177 + i * 4]); + ins->pan_env_points[i][1] = (int16_t)le16(&ih[177 + i * 4 + 2]); + } + + ins->vol_env_length = ih[225]; + ins->pan_env_length = ih[226]; + ins->vol_env_sustain = ih[227]; + ins->vol_env_loop_start = ih[228]; + ins->vol_env_loop_end = ih[229]; + ins->pan_env_sustain = ih[230]; + ins->pan_env_loop_start = ih[231]; + ins->pan_env_loop_end = ih[232]; + ins->vol_env_flags = ih[233]; + ins->pan_env_flags = ih[234]; + ins->auto_vib_type = ih[235]; + ins->auto_vib_sweep = ih[236]; + ins->auto_vib_depth = ih[237]; + ins->auto_vib_rate = ih[238]; + ins->fadeout = le16(&ih[239]); + ins->mute = (ih[247] == 1); + ins->num_samples = num_samples; + + int32_t headers_to_read = num_samples; + if (headers_to_read > MAX_SMP_PER_INST) { + headers_to_read = MAX_SMP_PER_INST; + } + + if (r->size - r->pos < (uint32_t)num_samples * 40) { + return false; + } + + for (int32_t i = 0; i < headers_to_read; i++) { + const uint8_t *h = &r->data[r->pos + i * 40]; + sample_t *s = &ins->smp[i]; + + s->length = (int32_t)le32(&h[0]); + s->loop_start = (int32_t)le32(&h[4]); + s->loop_length = (int32_t)le32(&h[8]); + s->volume = h[12]; + s->finetune = (int8_t)h[13]; + s->flags = h[14]; + s->panning = h[15]; + s->relative_note = (int8_t)h[16]; + + if (h[17] == 0xAD && !(h[14] & (SAMPLE_16BIT | SAMPLE_STEREO))) { + s->flags |= SAMPLE_ADPCM; + } + } + + for (int32_t i = MAX_SMP_PER_INST; i < num_samples; i++) { + extra_sample_lengths[i - MAX_SMP_PER_INST] = le32(&r->data[r->pos + i * 40]); + } + + skip_bytes(r, (uint32_t)num_samples * 40); + + return true; +} + +static bool skip_instrument(reader_t *r) { + uint8_t ih[INSTR_HEADER_SIZE]; + + for (int32_t i = 0; i < INSTR_HEADER_SIZE; i++) { + ih[i] = 0; + } + + if (r->size - r->pos < 4) { + return false; + } + + const uint32_t instr_size = le32(&r->data[r->pos]); + + uint32_t read_size = instr_size; + if (read_size == 0 || read_size > INSTR_HEADER_SIZE) { + read_size = INSTR_HEADER_SIZE; + } + + for (uint32_t i = 0; i < read_size; i++) { + ih[i] = read8(r); + } + + if (instr_size > INSTR_HEADER_SIZE) { + skip_bytes(r, instr_size - INSTR_HEADER_SIZE); + } + + const int16_t num_samples = (int16_t)le16(&ih[27]); + if (num_samples < 0 || num_samples > 32) { + return false; + } + + if (num_samples == 0) { + return true; + } + + if (r->size - r->pos < (uint32_t)num_samples * 40) { + return false; + } + + uint32_t data_bytes = 0; + for (int32_t i = 0; i < num_samples; i++) { + data_bytes += le32(&r->data[r->pos + i * 40]); + } + + skip_bytes(r, (uint32_t)num_samples * 40); + skip_bytes(r, data_bytes); + + return true; +} + +static void load_adpcm_sample(reader_t *r, sample_t *s) { + int8_t delta_lut[16]; + for (int32_t i = 0; i < 16; i++) { + delta_lut[i] = (int8_t)read8(r); + } + + int8_t *data_ptr = s->data_ptr; + const int32_t data_length = (s->length + 1) / 2; + + int8_t curr_sample = 0; + for (int32_t i = 0; i < data_length; i++) { + const uint8_t nibbles = read8(r); + + curr_sample += delta_lut[nibbles & 0x0F]; + *data_ptr++ = curr_sample; + + curr_sample += delta_lut[nibbles >> 4]; + *data_ptr++ = curr_sample; + } +} + +static void decode_delta8(int8_t *p, int32_t length, bool stereo) { + if (stereo) { + length >>= 1; + + int8_t *left = p; + int8_t *right = p + length; + int8_t old_left = 0; + int8_t old_right = 0; + + for (int32_t i = 0; i < length; i++) { + old_left = (int8_t)(left[i] + old_left); + old_right = (int8_t)(right[i] + old_right); + left[i] = (int8_t)((old_left + old_right) >> 1); + } + } else { + int8_t old = 0; + + for (int32_t i = 0; i < length; i++) { + old = (int8_t)(p[i] + old); + p[i] = old; + } + } +} + +static void decode_delta16(int16_t *p, int32_t length, bool stereo) { + if (stereo) { + length >>= 1; + + int16_t *left = p; + int16_t *right = p + length; + int16_t old_left = 0; + int16_t old_right = 0; + + for (int32_t i = 0; i < length; i++) { + old_left = (int16_t)(left[i] + old_left); + old_right = (int16_t)(right[i] + old_right); + left[i] = (int16_t)((old_left + old_right) >> 1); + } + } else { + int16_t old = 0; + + for (int32_t i = 0; i < length; i++) { + old = (int16_t)(p[i] + old); + p[i] = old; + } + } +} + +static bool load_instr_samples(reader_t *r, int32_t ins_num) { + instr_t *ins = instr[ins_num]; + if (ins == NULL) { + return true; + } + + int32_t num_samples = ins->num_samples; + if (num_samples > MAX_SMP_PER_INST) { + num_samples = MAX_SMP_PER_INST; + } + + sample_t *s = ins->smp; + for (int32_t i = 0; i < num_samples; i++, s++) { + if (s->length <= 0) { + s->length = 0; + s->loop_start = 0; + s->loop_length = 0; + s->flags = 0; + continue; + } + + const int32_t length_in_file = s->length; + const bool sample_16bit = !!(s->flags & SAMPLE_16BIT); + const bool stereo_sample = !!(s->flags & SAMPLE_STEREO); + const bool adpcm_sample = !!(s->flags & SAMPLE_ADPCM); + + if (sample_16bit) { + s->length >>= 1; + s->loop_start >>= 1; + s->loop_length >>= 1; + } + + if (s->length > MAX_SAMPLE_LEN) { + s->length = MAX_SAMPLE_LEN; + } + + const size_t bytes = (size_t)s->length * (sample_16bit ? 2 : 1) + 2; + s->data_ptr = (int8_t *)malloc(bytes); + if (s->data_ptr == NULL) { + return false; + } + + for (size_t j = 0; j < bytes; j++) { + s->data_ptr[j] = 0; + } + + if (adpcm_sample) { + load_adpcm_sample(r, s); + } else { + const uint32_t start_pos = r->pos; + + if (sample_16bit) { + int16_t *p16 = (int16_t *)s->data_ptr; + for (int32_t j = 0; j < s->length; j++) { + p16[j] = (int16_t)read16(r); + } + + decode_delta16(p16, s->length, stereo_sample); + } else { + for (int32_t j = 0; j < s->length; j++) { + s->data_ptr[j] = (int8_t)read8(r); + } + + decode_delta8(s->data_ptr, s->length, stereo_sample); + } + + const uint32_t consumed = r->pos - start_pos; + if (consumed < (uint32_t)length_in_file) { + skip_bytes(r, (uint32_t)length_in_file - consumed); + } + + if (stereo_sample) { + s->length >>= 1; + s->loop_start >>= 1; + s->loop_length >>= 1; + } + } + + s->flags &= ~(SAMPLE_STEREO | SAMPLE_ADPCM); + } + + for (int32_t i = 0; i < ins->num_samples - MAX_SMP_PER_INST; i++) { + skip_bytes(r, extra_sample_lengths[i]); + } + + return true; +} + +static bool load_patterns(reader_t *r, int32_t num_patterns, uint16_t xm_version, int32_t file_channels) { + for (int32_t i = 0; i < num_patterns; i++) { + if (r->size - r->pos < 8) { + return false; + } + + const int32_t header_size = (int32_t)le32(&r->data[r->pos]); + skip_bytes(r, 4); + skip_bytes(r, 1); + + int32_t num_rows; + if (xm_version == 0x0102) { + num_rows = read8(r) + 1; + } else { + num_rows = read16(r); + } + + const uint32_t data_size = read16(r); + + if (xm_version == 0x0102) { + if (header_size > 8) { + skip_bytes(r, (uint32_t)header_size - 8); + } + } else { + if (header_size > 9) { + skip_bytes(r, (uint32_t)header_size - 9); + } + } + + if (num_rows <= 0) { + num_rows = 64; + } + + if (num_rows > MAX_PATT_LEN) { + num_rows = MAX_PATT_LEN; + } + + pattern_num_rows[i] = (int16_t)num_rows; + + if (data_size == 0) { + pattern_num_rows[i] = 64; + continue; + } + + if (data_size > r->size - r->pos) { + return false; + } + + note_t *notes = (note_t *)malloc((size_t)num_rows * (size_t)song.num_channels * sizeof (note_t)); + if (notes == NULL) { + return false; + } + + for (int32_t j = 0; j < num_rows * song.num_channels; j++) { + notes[j] = (note_t){ 0 }; + } + + reader_t pr = { + r->data + r->pos, + data_size, + 0 + }; + + bool any_note = false; + + for (int32_t row = 0; row < num_rows; row++) { + for (int32_t c = 0; c < file_channels; c++) { + note_t n = { 0 }; + const uint8_t packed = read8(&pr); + + if (packed & 128) { + n.note = (packed & 1) ? read8(&pr) : 0; + n.instr = (packed & 2) ? read8(&pr) : 0; + n.vol = (packed & 4) ? read8(&pr) : 0; + n.efx = (packed & 8) ? read8(&pr) : 0; + n.efx_data = (packed & 16) ? read8(&pr) : 0; + } else { + n.note = packed; + n.instr = read8(&pr); + n.vol = read8(&pr); + n.efx = read8(&pr); + n.efx_data = read8(&pr); + } + + if (n.note > 97) { + n.note = 0; + } + + if (n.instr > MAX_INST) { + n.instr = 0; + } + + if (n.efx > 35) { + n.efx = 0; + n.efx_data = 0; + } + + if (c < song.num_channels) { + notes[row * song.num_channels + c] = n; + + if (n.note != 0 || n.instr != 0 || n.vol != 0 || n.efx != 0 || n.efx_data != 0) { + any_note = true; + } + } + } + } + + skip_bytes(r, data_size); + + if (any_note) { + pattern[i] = notes; + } else { + free(notes); + pattern_num_rows[i] = 64; + } + } + + return true; +} + +static bool load_xm(const uint8_t *data, uint32_t size) { + static const char xm_id[17] = { + 'E', + 'x', + 't', + 'e', + 'n', + 'd', + 'e', + 'd', + ' ', + 'M', + 'o', + 'd', + 'u', + 'l', + 'e', + ':', + ' ' + }; + + if (data == NULL || size < 336) { + return false; + } + + for (int32_t i = 0; i < 17; i++) { + if ((char)data[i] != xm_id[i]) { + return false; + } + } + + const uint16_t version = le16(&data[58]); + const uint32_t header_size = le32(&data[60]); + const uint16_t num_orders = le16(&data[64]); + const uint16_t song_loop_start = le16(&data[66]); + const uint16_t file_channels_raw = le16(&data[68]); + const uint16_t num_patterns = le16(&data[70]); + const uint16_t num_instr = le16(&data[72]); + const uint16_t flags = le16(&data[74]); + const uint16_t speed = le16(&data[76]); + const uint16_t bpm = le16(&data[78]); + + if (version < 0x0102 || version > 0x0104) { + return false; + } + + if (num_orders > MAX_ORDERS || num_patterns > MAX_PATTERNS) { + return false; + } + + if (file_channels_raw == 0 || num_instr > 256) { + return false; + } + + if (header_size > size - 60) { + return false; + } + + for (int32_t i = 0; i < MAX_PATTERNS; i++) { + pattern_num_rows[i] = 64; + } + + song = (song_t){ 0 }; + + song.song_length = num_orders; + song.song_loop_start = song_loop_start; + song.bpm = bpm; + song.speed = speed; + linear_periods_flag = !!(flags & 1); + note2_period_lut = linear_periods_flag ? linear_period_lut : amiga_period_lut; + + if (song.song_length == 0) { + song.song_length = 1; + } else { + for (int32_t i = 0; i < song.song_length; i++) { + song.orders[i] = data[80 + i]; + } + } + + for (int32_t i = 255; i >= 0; i--) { + if (song.orders[i] != 0xFF) { + break; + } + + if (song.song_length > i) { + song.song_length = (uint16_t)i; + } + } + + if (song.song_length > 255) { + song.song_length = 255; + } + + const int32_t file_channels = file_channels_raw; + int32_t num_channels = file_channels; + if (num_channels > MAX_CHANNELS) { + num_channels = MAX_CHANNELS; + } + + if (num_channels & 1) { + num_channels++; + } + + song.num_channels = CLAMP(num_channels, 2, MAX_CHANNELS); + + reader_t reader = { + data, + size, + 60 + header_size + }; + + reader_t *r = &reader; + + if (!allocate_instr(0)) { + return false; + } + + instr[0]->smp[0].volume = 0; + + if (version < 0x0104) { + if (num_instr > MAX_INST) { + return false; + } + + for (int32_t i = 0; i < num_instr; i++) { + if (!load_instr_header(r, 1 + i)) { + return false; + } + } + + if (!load_patterns(r, num_patterns, version, file_channels)) { + return false; + } + + for (int32_t i = 0; i < num_instr; i++) { + if (!load_instr_samples(r, 1 + i)) { + return false; + } + } + } else { + if (!load_patterns(r, num_patterns, version, file_channels)) { + return false; + } + + for (int32_t i = 0; i < num_instr; i++) { + const int32_t slot = (i < MAX_INST) ? (1 + i) : MAX_INST + 1; + + if (slot > MAX_INST) { + if (!skip_instrument(r)) { + return false; + } + + continue; + } + + if (!load_instr_header(r, slot)) { + return false; + } + + if (!load_instr_samples(r, slot)) { + return false; + } + } + } + + for (int32_t i = 1; i <= MAX_INST; i++) { + if (instr[i] == NULL) { + continue; + } + + sanitize_instrument(instr[i]); + for (int32_t j = 0; j < MAX_SMP_PER_INST; j++) { + sanitize_sample(&instr[i]->smp[j]); + } + } + + song.bpm = CLAMP(song.bpm, MIN_BPM, MAX_BPM); + song.initial_bpm = song.bpm; + song.initial_speed = song.speed = CLAMP(song.speed, 1, MAX_SPEED); + + if (song.song_loop_start >= song.song_length) { + song.song_loop_start = 0; + } + + song.global_volume = 64; + + return true; +} + +static void release_audio(void) { + if (audio_left >= 0) { + ta_audio_unlock(audio_left); + } + + if (audio_right >= 0) { + ta_audio_unlock(audio_right); + } + + audio_left = -1; + audio_right = -1; +} + +bool ta_ft2_init(const uint8_t *data, uint32_t length) { + ta_ft2_destroy(); + build_panning_table(); + + if (!load_xm(data, length)) { + free_song(); + return false; + } + + reset_channels(); + stop_voices(); + song_loaded = true; + + return true; +} + +bool ta_ft2_play(void) { + if (!song_loaded) { + return false; + } + + if (audio_left < 0) { + audio_left = ta_audio_lock(audio_source, NULL); + if (audio_left < 0) { + return false; + } + + audio_right = ta_audio_lock(audio_source, NULL); + + if (audio_right >= 0) { + ta_audio_set_pan(audio_left, TA_AUDIO_PAN_LEFT); + ta_audio_set_pan(audio_right, TA_AUDIO_PAN_RIGHT); + } + } + + ta_audio_clear(audio_left); + if (audio_right >= 0) { + ta_audio_clear(audio_right); + } + + reset_channels(); + stop_voices(); + + song.speed = song.initial_speed; + song.bpm = song.initial_bpm; + set_mixer_bpm(song.bpm); + + set_song_pos(0, 0); + reset_replayer_state(); + bxx_overflow = false; + tick_samples_left = 0; + tick_remainder = 0; + song_playing = true; + + return true; +} + +void ta_ft2_stop(void) { + song_playing = false; + release_audio(); + + if (song_loaded) { + stop_voices(); + } +} + +bool ta_ft2_is_playing(void) { + return song_playing; +} + +void ta_ft2_destroy(void) { + ta_ft2_stop(); + free_song(); +} diff --git a/src/main.c b/src/main.c @@ -1,3 +1,5 @@ +#include "audio.h" +#include "ft2.h" #include "display.h" #include "fb.h" #include "api.h" @@ -7,6 +9,7 @@ void ta_main_destroy(void) { int32_t exit_code = ta_vm_exit_code(); + ta_ft2_destroy(); ta_screen_destroy(); ta_vm_unload(); ta_display_deinit(); @@ -15,6 +18,8 @@ void ta_main_destroy(void) { } bool ta_main_update(void) { + ta_audio_update(); + bool drawn = ta_screen_update(); if (!ta_vm_running) { diff --git a/src/scene.c b/src/scene.c @@ -81,8 +81,12 @@ static ta_scene_op *push(void) { op->height = 0; op->frame = 0; op->shape_type = 0; - for (uint32_t i = 0; i < 6; i++) op->shape_args[i] = 0; - op->color = 0; + + for (uint32_t i = 0; i < 6; i++) { + op->shape_args[i] = 0; + } + + op->color = 0; op->sprites_after = 0; return op; @@ -129,11 +133,19 @@ bool ta_scene_image(const uint8_t *data, uint32_t length, ta_fixed x, ta_fixed y bool ta_scene_shape(uint32_t type, const uint32_t *args, uint32_t color) { ta_scene_op *op = push(); - if (op == NULL || args == NULL || type < 1 || type > 4) return false; - op->shape_type = type; + + if (op == NULL || args == NULL || type < 1 || type > 4) { + return false; + } + + op->shape_type = type; op->color = color; - for (uint32_t i = 0; i < 6; i++) op->shape_args[i] = args[i]; - return true; + + for (uint32_t i = 0; i < 6; i++) { + op->shape_args[i] = args[i]; + } + + return true; } bool ta_scene_text(ta_text *text, uint32_t x, uint32_t y) { @@ -163,7 +175,7 @@ bool ta_scene_end(void) { } if (prefix == now_count && prefix == before_count) { - return false; + return false; } } @@ -184,10 +196,15 @@ bool ta_scene_end(void) { if (op->shape_type != 0) { uint32_t *a = op->shape_args; - if (op->shape_type == 1) ta_shapes_draw_pixel(a[0], a[1], op->color); - else if (op->shape_type == 2) ta_shapes_draw_rectangle(a[0], a[1], a[2], a[3], op->color); - else if (op->shape_type == 3) ta_shapes_draw_triangle(a[0], a[1], a[2], a[3], a[4], a[5], op->color); - else ta_shapes_draw_oval(a[0], a[1], a[2], a[3], op->color); + if (op->shape_type == 1) { + ta_shapes_draw_pixel(a[0], a[1], op->color); + } else if (op->shape_type == 2) { + ta_shapes_draw_rectangle(a[0], a[1], a[2], a[3], op->color); + } else if (op->shape_type == 3) { + ta_shapes_draw_triangle(a[0], a[1], a[2], a[3], a[4], a[5], op->color); + } else { + ta_shapes_draw_oval(a[0], a[1], a[2], a[3], op->color); + } } else if (op->text != NULL) { op->text->x = op->x; op->text->y = op->y; diff --git a/src/syscall.c b/src/syscall.c @@ -9,8 +9,10 @@ #include "screen.h" #include "keyboard.h" #include "image.h" +#include "ft2.h" +#include "api.h" -static bool image_data(const ta_vm *vm, uint32_t address, const uint8_t **data, uint32_t *length) { +static bool get_data(const ta_vm *vm, uint32_t address, const uint8_t **data, uint32_t *length) { *data = (const uint8_t *)ta_vm_translate_pointer(vm, address, 1); if (*data == NULL) { @@ -158,7 +160,7 @@ ta_vm_status ta_syscall(ta_vm *vm) { const uint8_t *data; uint32_t length; - if (!image_data(vm, args[0], &data, &length)) { + if (!get_data(vm, args[0], &data, &length)) { return TA_VM_FAULT_MEMORY; } @@ -170,7 +172,7 @@ ta_vm_status ta_syscall(ta_vm *vm) { const uint8_t *data; uint32_t length; - if (!image_data(vm, args[0], &data, &length)) { + if (!get_data(vm, args[0], &data, &length)) { return TA_VM_FAULT_MEMORY; } @@ -184,7 +186,7 @@ ta_vm_status ta_syscall(ta_vm *vm) { uint32_t width; uint32_t height; - if (!image_data(vm, args[0], &data, &length)) { + if (!get_data(vm, args[0], &data, &length)) { return TA_VM_FAULT_MEMORY; } @@ -266,6 +268,33 @@ ta_vm_status ta_syscall(ta_vm *vm) { result = ta_scene_shape(3, coordinates, args[1]) ? 1 : 0; break; } + case TA_SYS_FT2_LOAD: { + const uint8_t *data; + uint32_t length; + + if (!get_data(vm, args[0], &data, &length)) { + return TA_VM_FAULT_MEMORY; + } + + result = ta_ft2_init(data, length) ? 1 : 0; + + break; + } + case TA_SYS_FT2_PLAY: { + result = ta_ft2_play() ? 1 : 0; + + break; + } + case TA_SYS_FT2_STOP: { + ta_ft2_stop(); + + break; + } + case TA_SYS_FT2_IS_PLAYING: { + result = ta_ft2_is_playing() ? 1 : 0; + + break; + } default: { return TA_VM_FAULT_ILLEGAL; } @@ -277,6 +306,7 @@ ta_vm_status ta_syscall(ta_vm *vm) { } void ta_syscall_reset(void) { + ta_ft2_destroy(); ta_scene_invalidate(); ta_image_release();