ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

commit 28afa0b69e88eb2fb9971338896d0a78e88e3977
parent 873935c041099850da2ae29fc1109ed329ce71a4
Author: Olav Sørensen <olav.sorensen@live.no>
Date:   Sun,  3 Nov 2024 19:28:06 +0100

Some scope changes

Diffstat:
Msrc/scopes/ft2_scope_macros.h | 241+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/scopes/ft2_scopedraw.c | 213++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/scopes/ft2_scopes.c | 74++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/scopes/ft2_scopes.h | 7+++----
4 files changed, 323 insertions(+), 212 deletions(-)

diff --git a/src/scopes/ft2_scope_macros.h b/src/scopes/ft2_scope_macros.h @@ -1,6 +1,8 @@ #pragma once #include <stdint.h> +#include "../ft2_header.h" +#include "../mixer/ft2_windowed_sinc.h" #include "ft2_scopes.h" /* ----------------------------------------------------------------------- */ @@ -10,113 +12,72 @@ #define SCOPE_REGS_NO_LOOP \ const int32_t volume = s->volume; \ const int32_t sampleEnd = s->sampleEnd; \ - const uint32_t delta = s->drawDelta; \ + const uint64_t delta = s->drawDelta; \ const uint32_t color = video.palette[PAL_PATTEXT]; \ - const uint32_t width = x + w; \ + uint32_t width = x + w; \ int32_t sample; \ int32_t position = s->position; \ - uint32_t positionFrac = 0; + uint64_t positionFrac = 0; #define SCOPE_REGS_LOOP \ const int32_t volume = s->volume; \ const int32_t sampleEnd = s->sampleEnd; \ const int32_t loopStart = s->loopStart; \ const int32_t loopLength = s->loopLength; \ - const uint32_t delta = s->drawDelta; \ + const uint64_t delta = s->drawDelta; \ const uint32_t color = video.palette[PAL_PATTEXT]; \ - const uint32_t width = x + w; \ + uint32_t width = x + w; \ int32_t sample; \ int32_t position = s->position; \ - uint32_t positionFrac = 0; + uint64_t positionFrac = 0; -#define SCOPE_REGS_PINGPONG \ +#define SCOPE_REGS_BIDI \ const int32_t volume = s->volume; \ const int32_t sampleEnd = s->sampleEnd; \ const int32_t loopStart = s->loopStart; \ const int32_t loopLength = s->loopLength; \ - const uint32_t delta = s->drawDelta; \ + const uint64_t delta = s->drawDelta; \ const uint32_t color = video.palette[PAL_PATTEXT]; \ - const uint32_t width = x + w; \ + uint32_t width = x + w; \ int32_t sample; \ - int32_t position = s->position; \ - uint32_t positionFrac = 0; \ - int32_t direction = s->direction; + int32_t actualPos, position = s->position; \ + uint64_t positionFrac = 0; \ + bool samplingBackwards = s->samplingBackwards; #define LINED_SCOPE_REGS_NO_LOOP \ - const int32_t volume = s->volume; \ - const int32_t sampleEnd = s->sampleEnd; \ - const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ - const uint32_t width = (x + w) - 1; \ - int32_t sample, sample2; \ - int32_t y1, y2; \ - int32_t position = s->position; \ - uint32_t positionFrac = 0; + SCOPE_REGS_NO_LOOP \ + int32_t smpY1, smpY2; \ + width--; #define LINED_SCOPE_REGS_LOOP \ - const int32_t volume = s->volume; \ - const int32_t sampleEnd = s->sampleEnd; \ - const int32_t loopStart = s->loopStart; \ - const int32_t loopLength = s->loopLength; \ - const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ - const uint32_t width = (x + w) - 1; \ - int32_t sample, sample2; \ - int32_t y1, y2; \ - int32_t position = s->position; \ - uint32_t positionFrac = 0; + SCOPE_REGS_LOOP \ + int32_t smpY1, smpY2; \ + width--; -#define LINED_SCOPE_REGS_PINGPONG \ - const int32_t volume = s->volume; \ - const int32_t sampleEnd = s->sampleEnd; \ - const int32_t loopStart = s->loopStart; \ - const int32_t loopLength = s->loopLength; \ - const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ - const uint32_t width = (x + w) - 1; \ - int32_t sample, sample2; \ - int32_t y1, y2; \ - int32_t position = s->position; \ - uint32_t positionFrac = 0; \ - int32_t direction = s->direction; +#define LINED_SCOPE_REGS_BIDI \ + SCOPE_REGS_BIDI \ + int32_t smpY1, smpY2; \ + width--; -/* Note: Sample data already has a fixed tap samples at the end of the sample, -** so that an out-of-bounds read is OK and reads the correct interpolation tap. +/* Note: Sample data already has fixed tap samples at the end of the sample, +** so that out-of-bounds reads get the correct interpolation tap data. */ -#define COLLECT_LERP_SAMPLES8 \ - sample = s->base8[position+0] << 8; \ - sample2 = s->base8[position+1] << 8; - -#define COLLECT_LERP_SAMPLES16 \ - sample = s->base16[position+0]; \ - sample2 = s->base16[position+1]; - -#define DO_LERP \ - const int32_t frac = (uint32_t)positionFrac >> 1; /* 0..32767 */ \ - sample2 -= sample; \ - sample2 = (sample2 * frac) >> 15; \ - sample += sample2; \ - -#define DO_LERP_BIDI \ - int32_t frac = (uint32_t)positionFrac >> 1; /* 0..32767 */ \ - if (direction == -1) frac ^= 32767; /* negate frac */ \ - sample2 -= sample; \ - sample2 = (sample2 * frac) >> 15; \ - sample += sample2; - -#define GET_LERP_SMP8 \ - COLLECT_LERP_SAMPLES8 \ - DO_LERP - -#define GET_LERP_SMP16 \ - COLLECT_LERP_SAMPLES16 \ - DO_LERP - -#define GET_LERP_SMP8_BIDI \ - COLLECT_LERP_SAMPLES8 \ - DO_LERP_BIDI - -#define GET_LERP_SMP16_BIDI \ - COLLECT_LERP_SAMPLES16 \ - DO_LERP_BIDI +#define INTERPOLATE_SMP8(pos, frac) \ + const int8_t *s8 = s->base8 + pos; \ + const int16_t *t = scopeGaussianLUT + (((frac) >> (SCOPE_FRAC_BITS-8)) << 2); \ + sample = ((s8[0] * t[0]) + \ + (s8[1] * t[1]) + \ + (s8[2] * t[2]) + \ + (s8[3] * t[3])) >> (15-8); + +#define INTERPOLATE_SMP16(pos, frac) \ + const int16_t *s16 = s->base16 + pos; \ + const int16_t *t = scopeGaussianLUT + (((frac) >> (SCOPE_FRAC_BITS-8)) << 2); \ + sample = ((s16[0] * t[0]) + \ + (s16[1] * t[1]) + \ + (s16[2] * t[2]) + \ + (s16[3] * t[3])) >> 15; #define SCOPE_GET_SMP8 \ if (s->active) \ @@ -130,21 +91,32 @@ else \ sample = 0; -#define SCOPE_GET_LERP_SMP8 \ +#define SCOPE_GET_SMP8_BIDI \ if (s->active) \ { \ - GET_LERP_SMP8 \ - sample = (sample * volume) >> 16; \ + GET_BIDI_POSITION \ + sample = (s->base8[actualPos] * volume) >> 8; \ + } \ + else \ + { \ + sample = 0; \ + } + +#define SCOPE_GET_SMP16_BIDI \ + if (s->active) \ + { \ + GET_BIDI_POSITION \ + sample = (s->base16[actualPos] * volume) >> 16; \ } \ else \ { \ sample = 0; \ } -#define SCOPE_GET_LERP_SMP16 \ +#define SCOPE_GET_INTERPOLATED_SMP8 \ if (s->active) \ { \ - GET_LERP_SMP16 \ + INTERPOLATE_SMP8(position, (uint32_t)positionFrac) \ sample = (sample * volume) >> 16; \ } \ else \ @@ -152,10 +124,10 @@ sample = 0; \ } -#define SCOPE_GET_LERP_SMP8_BIDI \ +#define SCOPE_GET_INTERPOLATED_SMP16 \ if (s->active) \ { \ - GET_LERP_SMP8_BIDI \ + INTERPOLATE_SMP16(position, (uint32_t)positionFrac) \ sample = (sample * volume) >> 16; \ } \ else \ @@ -163,10 +135,17 @@ sample = 0; \ } -#define SCOPE_GET_LERP_SMP16_BIDI \ +#define GET_BIDI_POSITION \ + if (samplingBackwards) \ + actualPos = (sampleEnd - 1) - (position - loopStart); \ + else \ + actualPos = position; + +#define SCOPE_GET_INTERPOLATED_SMP8_BIDI \ if (s->active) \ { \ - GET_LERP_SMP16_BIDI \ + GET_BIDI_POSITION \ + INTERPOLATE_SMP8(actualPos, samplingBackwards ? ((uint32_t)positionFrac ^ UINT32_MAX) : positionFrac) \ sample = (sample * volume) >> 16; \ } \ else \ @@ -174,43 +153,50 @@ sample = 0; \ } -#define SCOPE_UPDATE_DRAWPOS \ - positionFrac += delta; \ - position += positionFrac >> 16; \ - positionFrac &= 0xFFFF; +#define SCOPE_GET_INTERPOLATED_SMP16_BIDI \ + if (s->active) \ + { \ + GET_BIDI_POSITION \ + INTERPOLATE_SMP16(actualPos, samplingBackwards ? ((uint32_t)positionFrac ^ UINT32_MAX) : positionFrac) \ + sample = (sample * volume) >> 16; \ + } \ + else \ + { \ + sample = 0; \ + } -#define SCOPE_UPDATE_DRAWPOS_PINGPONG \ +#define SCOPE_UPDATE_READPOS \ positionFrac += delta; \ - position += (int32_t)(positionFrac >> 16) * direction; \ - positionFrac &= 0xFFFF; + position += positionFrac >> 32; \ + positionFrac &= UINT32_MAX; #define SCOPE_DRAW_SMP \ video.frameBuffer[((lineY - sample) * SCREEN_W) + x] = color; -#define LINED_SCOPE_PREPARE_LERP_SMP8 \ - SCOPE_GET_LERP_SMP8 \ - y1 = lineY - sample; \ - SCOPE_UPDATE_DRAWPOS +#define LINED_SCOPE_PREPARE_SMP8 \ + SCOPE_GET_INTERPOLATED_SMP8 \ + smpY1 = lineY - sample; \ + SCOPE_UPDATE_READPOS -#define LINED_SCOPE_PREPARE_LERP_SMP16 \ - SCOPE_GET_LERP_SMP16 \ - y1 = lineY - sample; \ - SCOPE_UPDATE_DRAWPOS +#define LINED_SCOPE_PREPARE_SMP16 \ + SCOPE_GET_INTERPOLATED_SMP16 \ + smpY1 = lineY - sample; \ + SCOPE_UPDATE_READPOS -#define LINED_SCOPE_PREPARE_LERP_SMP8_BIDI \ - SCOPE_GET_LERP_SMP8_BIDI \ - y1 = lineY - sample; \ - SCOPE_UPDATE_DRAWPOS +#define LINED_SCOPE_PREPARE_SMP8_BIDI \ + SCOPE_GET_INTERPOLATED_SMP8_BIDI \ + smpY1 = lineY - sample; \ + SCOPE_UPDATE_READPOS -#define LINED_SCOPE_PREPARE_LERP_SMP16_BIDI \ - SCOPE_GET_LERP_SMP16_BIDI \ - y1 = lineY - sample; \ - SCOPE_UPDATE_DRAWPOS +#define LINED_SCOPE_PREPARE_SMP16_BIDI \ + SCOPE_GET_INTERPOLATED_SMP16_BIDI \ + smpY1 = lineY - sample; \ + SCOPE_UPDATE_READPOS #define LINED_SCOPE_DRAW_SMP \ - y2 = lineY - sample; \ - scopeLine(x, y1, y2); \ - y1 = y2; \ + smpY2 = lineY - sample; \ + scopeLine(x, smpY1, smpY2, color); \ + smpY1 = smpY2; #define SCOPE_HANDLE_POS_NO_LOOP \ if (position >= sampleEnd) \ @@ -225,22 +211,19 @@ position = loopStart; \ } -#define SCOPE_HANDLE_POS_PINGPONG \ - if (direction == -1 && position < loopStart) \ +#define SCOPE_HANDLE_POS_BIDI \ + if (position >= sampleEnd) \ { \ - direction = 1; /* change direction to forwards */ \ - \ if (loopLength >= 2) \ - position = loopStart + ((loopStart - position - 1) % loopLength); \ + { \ + const uint32_t overflow = position - sampleEnd; \ + const uint32_t cycles = overflow / loopLength; \ + const uint32_t phase = overflow % loopLength; \ + position = loopStart + phase; \ + samplingBackwards ^= !(cycles & 1); \ + } \ else \ + { \ position = loopStart; \ - } \ - else if (position >= sampleEnd) \ - { \ - direction = -1; /* change direction to backwards */ \ - \ - if (loopLength >= 2) \ - position = (sampleEnd - 1) - ((position - sampleEnd) % loopLength); \ - else \ - position = sampleEnd - 1; \ + } \ } diff --git a/src/scopes/ft2_scopedraw.c b/src/scopes/ft2_scopedraw.c @@ -1,10 +1,146 @@ #include "../ft2_video.h" #include "../ft2_palette.h" +#include "../mixer/ft2_gaussian.h" #include "ft2_scopes.h" #include "ft2_scopedraw.h" #include "ft2_scope_macros.h" -static void scopeLine(int32_t x1, int32_t y1, int32_t y2); +/* 15-bit Gaussian interpolation LUT with no overshoot (sum <= 1.0). +** Suitable for tracker scopes. +*/ +static const int16_t scopeGaussianLUT[4 * 256] = +{ + 4807,22963, 4871, -1, 4744,22962, 4935, -1, + 4681,22960, 5000, -1, 4619,22957, 5065, -1, + 4557,22953, 5131, -1, 4495,22948, 5197, -1, + 4435,22942, 5264, -1, 4374,22935, 5332, -1, + 4315,22927, 5399, -1, 4255,22918, 5468, -1, + 4197,22908, 5536, -1, 4138,22897, 5606, -1, + 4081,22885, 5676, -1, 4023,22872, 5746, -1, + 3967,22857, 5817, -1, 3910,22842, 5888, -1, + 3855,22826, 5959, 0, 3799,22809, 6032, 0, + 3745,22791, 6104, 0, 3691,22772, 6177, 0, + 3637,22752, 6251, 0, 3584,22731, 6325, 0, + 3531,22709, 6400, 0, 3479,22686, 6475, 1, + 3427,22662, 6550, 1, 3376,22637, 6626, 1, + 3325,22611, 6702, 1, 3275,22584, 6779, 2, + 3225,22556, 6856, 2, 3176,22527, 6934, 2, + 3128,22498, 7012, 3, 3079,22467, 7091, 3, + 3032,22435, 7170, 3, 2985,22402, 7249, 4, + 2938,22369, 7329, 4, 2892,22334, 7409, 5, + 2846,22299, 7490, 5, 2801,22262, 7571, 6, + 2756,22225, 7653, 7, 2712,22187, 7735, 7, + 2668,22148, 7817, 8, 2624,22107, 7900, 9, + 2582,22066, 7983, 9, 2539,22025, 8066, 10, + 2497,21982, 8150, 11, 2456,21938, 8234, 12, + 2415,21893, 8319, 13, 2374,21848, 8404, 14, + 2334,21801, 8489, 15, 2295,21754, 8575, 16, + 2256,21706, 8661, 17, 2217,21657, 8748, 18, + 2179,21607, 8834, 19, 2141,21556, 8922, 21, + 2104,21505, 9009, 22, 2067,21452, 9097, 24, + 2031,21399, 9185, 25, 1995,21345, 9273, 27, + 1959,21290, 9362, 28, 1924,21235, 9451, 30, + 1890,21178, 9541, 32, 1856,21121, 9630, 33, + 1822,21063, 9720, 35, 1789,21004, 9811, 37, + 1756,20944, 9901, 39, 1723,20884, 9992, 41, + 1691,20822,10083, 44, 1660,20760,10174, 46, + 1628,20698,10266, 48, 1598,20634,10358, 51, + 1567,20570,10450, 53, 1537,20505,10542, 56, + 1508,20439,10635, 58, 1479,20373,10727, 61, + 1450,20306,10820, 64, 1422,20238,10913, 67, + 1394,20169,11007, 70, 1366,20100,11100, 73, + 1339,20030,11194, 77, 1312,19959,11288, 80, + 1286,19888,11382, 84, 1260,19816,11476, 87, + 1234,19744,11571, 91, 1209,19671,11665, 95, + 1184,19597,11760, 99, 1160,19522,11855, 103, + 1136,19447,11950, 107, 1112,19372,12045, 111, + 1089,19295,12140, 116, 1066,19219,12236, 120, + 1043,19141,12331, 125, 1020,19063,12427, 130, + 999,18985,12522, 135, 977,18905,12618, 140, + 956,18826,12714, 145, 935,18746,12809, 150, + 914,18665,12905, 156, 894,18584,13001, 161, + 874,18502,13097, 167, 854,18420,13193, 173, + 835,18337,13289, 179, 816,18254,13385, 186, + 797,18170,13481, 192, 779,18086,13577, 199, + 761,18001,13673, 205, 743,17916,13769, 212, + 726,17830,13865, 219, 708,17744,13961, 227, + 692,17658,14056, 234, 675,17571,14152, 242, + 659,17484,14248, 250, 643,17396,14343, 257, + 627,17308,14439, 266, 612,17220,14534, 274, + 597,17131,14630, 283, 582,17042,14725, 291, + 567,16953,14820, 300, 553,16863,14915, 309, + 539,16773,15010, 319, 525,16682,15104, 328, + 512,16592,15199, 338, 498,16500,15293, 348, + 485,16409,15387, 358, 473,16317,15481, 369, + 460,16226,15575, 379, 448,16133,15669, 390, + 436,16041,15762, 401, 424,15948,15855, 412, + 412,15855,15948, 424, 401,15762,16041, 436, + 390,15669,16133, 448, 379,15575,16226, 460, + 369,15481,16317, 473, 358,15387,16409, 485, + 348,15293,16500, 498, 338,15199,16592, 512, + 328,15104,16682, 525, 319,15010,16773, 539, + 309,14915,16863, 553, 300,14820,16953, 567, + 291,14725,17042, 582, 283,14630,17131, 597, + 274,14534,17220, 612, 266,14439,17308, 627, + 257,14343,17396, 643, 250,14248,17484, 659, + 242,14152,17571, 675, 234,14056,17658, 692, + 227,13961,17744, 708, 219,13865,17830, 726, + 212,13769,17916, 743, 205,13673,18001, 761, + 199,13577,18086, 779, 192,13481,18170, 797, + 186,13385,18254, 816, 179,13289,18337, 835, + 173,13193,18420, 854, 167,13097,18502, 874, + 161,13001,18584, 894, 156,12905,18665, 914, + 150,12809,18746, 935, 145,12714,18826, 956, + 140,12618,18905, 977, 135,12522,18985, 999, + 130,12427,19063, 1020, 125,12331,19141, 1043, + 120,12236,19219, 1066, 116,12140,19295, 1089, + 111,12045,19372, 1112, 107,11950,19447, 1136, + 103,11855,19522, 1160, 99,11760,19597, 1184, + 95,11665,19671, 1209, 91,11571,19744, 1234, + 87,11476,19816, 1260, 84,11382,19888, 1286, + 80,11288,19959, 1312, 77,11194,20030, 1339, + 73,11100,20100, 1366, 70,11007,20169, 1394, + 67,10913,20238, 1422, 64,10820,20306, 1450, + 61,10727,20373, 1479, 58,10635,20439, 1508, + 56,10542,20505, 1537, 53,10450,20570, 1567, + 51,10358,20634, 1598, 48,10266,20698, 1628, + 46,10174,20760, 1660, 44,10083,20822, 1691, + 41, 9992,20884, 1723, 39, 9901,20944, 1756, + 37, 9811,21004, 1789, 35, 9720,21063, 1822, + 33, 9630,21121, 1856, 32, 9541,21178, 1890, + 30, 9451,21235, 1924, 28, 9362,21290, 1959, + 27, 9273,21345, 1995, 25, 9185,21399, 2031, + 24, 9097,21452, 2067, 22, 9009,21505, 2104, + 21, 8922,21556, 2141, 19, 8834,21607, 2179, + 18, 8748,21657, 2217, 17, 8661,21706, 2256, + 16, 8575,21754, 2295, 15, 8489,21801, 2334, + 14, 8404,21848, 2374, 13, 8319,21893, 2415, + 12, 8234,21938, 2456, 11, 8150,21982, 2497, + 10, 8066,22025, 2539, 9, 7983,22066, 2582, + 9, 7900,22107, 2624, 8, 7817,22148, 2668, + 7, 7735,22187, 2712, 7, 7653,22225, 2756, + 6, 7571,22262, 2801, 5, 7490,22299, 2846, + 5, 7409,22334, 2892, 4, 7329,22369, 2938, + 4, 7249,22402, 2985, 3, 7170,22435, 3032, + 3, 7091,22467, 3079, 3, 7012,22498, 3128, + 2, 6934,22527, 3176, 2, 6856,22556, 3225, + 2, 6779,22584, 3275, 1, 6702,22611, 3325, + 1, 6626,22637, 3376, 1, 6550,22662, 3427, + 1, 6475,22686, 3479, 0, 6400,22709, 3531, + 0, 6325,22731, 3584, 0, 6251,22752, 3637, + 0, 6177,22772, 3691, 0, 6104,22791, 3745, + 0, 6032,22809, 3799, 0, 5959,22826, 3855, + -1, 5888,22842, 3910, -1, 5817,22857, 3967, + -1, 5746,22872, 4023, -1, 5676,22885, 4081, + -1, 5606,22897, 4138, -1, 5536,22908, 4197, + -1, 5468,22918, 4255, -1, 5399,22927, 4315, + -1, 5332,22935, 4374, -1, 5264,22942, 4435, + -1, 5197,22948, 4495, -1, 5131,22953, 4557, + -1, 5065,22957, 4619, -1, 5000,22960, 4681, + -1, 4935,22962, 4744, -1, 4871,22963, 4807 +}; + +static void scopeLine(int32_t x1, int32_t y1, int32_t y2, uint32_t color); /* ----------------------------------------------------------------------- */ /* SCOPE DRAWING ROUTINES */ @@ -18,7 +154,7 @@ static void scopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_ { SCOPE_GET_SMP8 SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_NO_LOOP } } @@ -31,21 +167,21 @@ static void scopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t { SCOPE_GET_SMP8 SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_LOOP } } static void scopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { - SCOPE_REGS_PINGPONG + SCOPE_REGS_BIDI for (; x < width; x++) { - SCOPE_GET_SMP8 + SCOPE_GET_SMP8_BIDI SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG + SCOPE_UPDATE_READPOS + SCOPE_HANDLE_POS_BIDI } } @@ -57,7 +193,7 @@ static void scopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32 { SCOPE_GET_SMP16 SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_NO_LOOP } } @@ -70,21 +206,21 @@ static void scopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t { SCOPE_GET_SMP16 SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_LOOP } } static void scopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { - SCOPE_REGS_PINGPONG + SCOPE_REGS_BIDI for (; x < width; x++) { - SCOPE_GET_SMP16 + SCOPE_GET_SMP16_BIDI SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG + SCOPE_UPDATE_READPOS + SCOPE_HANDLE_POS_BIDI } } @@ -95,14 +231,14 @@ static void scopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint static void linedScopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { LINED_SCOPE_REGS_NO_LOOP - LINED_SCOPE_PREPARE_LERP_SMP8 + LINED_SCOPE_PREPARE_SMP8 SCOPE_HANDLE_POS_NO_LOOP for (; x < width; x++) { - SCOPE_GET_LERP_SMP8 + SCOPE_GET_INTERPOLATED_SMP8 LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_NO_LOOP } } @@ -110,44 +246,44 @@ static void linedScopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, ui static void linedScopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { LINED_SCOPE_REGS_LOOP - LINED_SCOPE_PREPARE_LERP_SMP8 + LINED_SCOPE_PREPARE_SMP8 SCOPE_HANDLE_POS_LOOP for (; x < width; x++) { - SCOPE_GET_LERP_SMP8 + SCOPE_GET_INTERPOLATED_SMP8 LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_LOOP } } static void linedScopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { - LINED_SCOPE_REGS_PINGPONG - LINED_SCOPE_PREPARE_LERP_SMP8_BIDI - SCOPE_HANDLE_POS_PINGPONG + LINED_SCOPE_REGS_BIDI + LINED_SCOPE_PREPARE_SMP8_BIDI + SCOPE_HANDLE_POS_BIDI for (; x < width; x++) { - SCOPE_GET_LERP_SMP8_BIDI + SCOPE_GET_INTERPOLATED_SMP8_BIDI LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG + SCOPE_UPDATE_READPOS + SCOPE_HANDLE_POS_BIDI } } static void linedScopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { LINED_SCOPE_REGS_NO_LOOP - LINED_SCOPE_PREPARE_LERP_SMP16 + LINED_SCOPE_PREPARE_SMP16 SCOPE_HANDLE_POS_NO_LOOP for (; x < width; x++) { - SCOPE_GET_LERP_SMP16 + SCOPE_GET_INTERPOLATED_SMP16 LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_NO_LOOP } } @@ -155,40 +291,39 @@ static void linedScopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, u static void linedScopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { LINED_SCOPE_REGS_LOOP - LINED_SCOPE_PREPARE_LERP_SMP16 + LINED_SCOPE_PREPARE_SMP16 SCOPE_HANDLE_POS_LOOP for (; x < width; x++) { - SCOPE_GET_LERP_SMP16 + SCOPE_GET_INTERPOLATED_SMP16 LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS + SCOPE_UPDATE_READPOS SCOPE_HANDLE_POS_LOOP } } static void linedScopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) { - LINED_SCOPE_REGS_PINGPONG - LINED_SCOPE_PREPARE_LERP_SMP16_BIDI - SCOPE_HANDLE_POS_PINGPONG + LINED_SCOPE_REGS_BIDI + LINED_SCOPE_PREPARE_SMP16_BIDI + SCOPE_HANDLE_POS_BIDI for (; x < width; x++) { - SCOPE_GET_LERP_SMP16_BIDI + SCOPE_GET_INTERPOLATED_SMP16_BIDI LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG + SCOPE_UPDATE_READPOS + SCOPE_HANDLE_POS_BIDI } } // ----------------------------------------------------------------------- -static void scopeLine(int32_t x1, int32_t y1, int32_t y2) +static void scopeLine(int32_t x1, int32_t y1, int32_t y2, uint32_t color) { const int32_t dy = y2 - y1; const int32_t sy = SGN(dy); - const uint32_t color = video.palette[PAL_PATTEXT]; const int32_t pitch = sy * SCREEN_W; uint32_t *dst32 = &video.frameBuffer[(y1 * SCREEN_W) + x1]; diff --git a/src/scopes/ft2_scopes.c b/src/scopes/ft2_scopes.c @@ -40,14 +40,21 @@ int32_t getSamplePosition(uint8_t ch) // cache some stuff volatile bool active = sc->active; + volatile bool samplingBackwards = sc->samplingBackwards; volatile int32_t position = sc->position; + volatile int32_t loopStart = sc->loopStart; volatile int32_t sampleEnd = sc->sampleEnd; if (!active || sampleEnd == 0) return -1; if (position >= 0 && position < sampleEnd) + { + if (samplingBackwards) // get actual bidi pos when in backwards mode + position = (sampleEnd - 1) - (position - loopStart); + return position; + } return -1; // not active or overflown } @@ -307,7 +314,7 @@ static void scopeTrigger(int32_t ch, const sample_t *s, int32_t playOffset) tempState.sample16Bit = sample16Bit; tempState.loopType = loopType; - tempState.direction = 1; // forwards + tempState.samplingBackwards = false; tempState.sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd; tempState.loopStart = loopStart; tempState.loopLength = loopLength; @@ -347,52 +354,41 @@ static void updateScopes(void) // scope position update s.positionFrac += s.delta; - const uint32_t wholeSamples = (uint32_t)(s.positionFrac >> SCOPE_FRAC_BITS); + s.position += s.positionFrac >> SCOPE_FRAC_BITS; s.positionFrac &= SCOPE_FRAC_MASK; - if (s.direction == 1) - s.position += wholeSamples; // forwards - else - s.position -= wholeSamples; // backwards - - // handle loop wrapping or sample end - if (s.direction == -1 && s.position < s.loopStart) // sampling backwards (definitely pingpong loop) - { - s.direction = 1; // change direction to forwards - - if (s.loopLength >= 2) - s.position = s.loopStart + ((s.loopStart - s.position - 1) % s.loopLength); - else - s.position = s.loopStart; - - assert(s.position >= s.loopStart && s.position < s.sampleEnd); - } - else if (s.position >= s.sampleEnd) + if (s.position >= s.sampleEnd) { - uint32_t loopOverflowVal; - - if (s.loopLength >= 2) - loopOverflowVal = (s.position - s.sampleEnd) % s.loopLength; - else - loopOverflowVal = 0; - - if (s.loopType == LOOP_DISABLED) + if (s.loopType == LOOP_BIDI) { - s.active = false; + if (s.loopLength >= 2) + { + // wrap as forward loop (position is inverted if sampling backwards, when needed) + + const uint32_t overflow = s.position - s.sampleEnd; + const uint32_t cycles = overflow / s.loopLength; + const uint32_t phase = overflow % s.loopLength; + + s.position = s.loopStart + phase; + s.samplingBackwards ^= !(cycles & 1); + } + else + { + s.position = s.loopStart; + } } else if (s.loopType == LOOP_FORWARD) { - s.position = s.loopStart + loopOverflowVal; - assert(s.position >= s.loopStart && s.position < s.sampleEnd); + if (s.loopLength >= 2) + s.position = s.loopStart + ((s.position - s.sampleEnd) % s.loopLength); + else + s.position = s.loopStart; } - else // pingpong loop + else // no loop { - s.direction = -1; // change direction to backwards - s.position = (s.sampleEnd - 1) - loopOverflowVal; - assert(s.position >= s.loopStart && s.position < s.sampleEnd); + s.active = false; } } - assert(s.position >= 0); *sc = s; // set new scope state } @@ -432,10 +428,8 @@ void drawScopes(void) // scope is active scope[i].wasCleared = false; - // get relative voice Hz (in relation to middle-C rate), and scale to 16.16fp - const uint32_t relHz16 = (uint32_t)(scope[i].delta * (((double)SCOPE_HZ / SCOPE_FRAC_SCALE) / (C4_FREQ / 65536.0))); - - scope[i].drawDelta = relHz16 << 1; // FT2 does this to the final 16.16fp value + // get relative voice Hz (in relation to C4/2 rate) + scope[i].drawDelta = (uint64_t)(scope[i].delta * ((double)SCOPE_HZ / ((double)C4_FREQ / 2.0))); // clear scope background clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT); diff --git a/src/scopes/ft2_scopes.h b/src/scopes/ft2_scopes.h @@ -25,11 +25,10 @@ typedef struct scope_t volatile bool active; const int8_t *base8; const int16_t *base16; - bool wasCleared, sample16Bit; + bool wasCleared, sample16Bit, samplingBackwards; uint8_t loopType; - int32_t volume, direction, loopStart, loopLength, sampleEnd, position; - uint32_t drawDelta; - uint64_t delta, positionFrac; + int32_t volume, loopStart, loopLength, sampleEnd, position; + uint64_t delta, drawDelta, positionFrac; } scope_t; typedef struct lastChInstr_t