diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2020-09-26 17:19:07 -0400 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2020-10-09 11:39:25 -0400 |
commit | 4231c2c83f2b5331e3e38b10a308ee3752315f9c (patch) | |
tree | 1e14867e9c9f0d7b778e7c4c18103a7cbd491794 /lib | |
parent | 278522f8118bd2cfce065ec95f0a93ca53e3ca44 (diff) |
codecs: Add support for the 'VTX' ZX Spectrum chiptunes format.
This codec requires floating point.
Original author: Peter Sovietov
Ported to Rockbox: Roman Skylarov
Further integration and bugfixes: Solomon Peachy
Change-Id: I781ecd3592dfcdbbc694063334350342534f1d6c
Diffstat (limited to 'lib')
-rw-r--r-- | lib/rbcodec/SOURCES | 3 | ||||
-rw-r--r-- | lib/rbcodec/codecs/SOURCES | 3 | ||||
-rw-r--r-- | lib/rbcodec/codecs/codecs.make | 3 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/SOURCES | 5 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/ayumi.c | 361 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/ayumi.h | 72 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/ayumi_render.c | 328 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/ayumi_render.h | 84 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/libayumi.make | 18 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/lzh.c | 420 | ||||
-rw-r--r-- | lib/rbcodec/codecs/libayumi/lzh.h | 6 | ||||
-rw-r--r-- | lib/rbcodec/codecs/vtx.c | 138 | ||||
-rw-r--r-- | lib/rbcodec/metadata/metadata.c | 40 | ||||
-rw-r--r-- | lib/rbcodec/metadata/metadata.h | 13 | ||||
-rw-r--r-- | lib/rbcodec/metadata/metadata_parsers.h | 3 | ||||
-rw-r--r-- | lib/rbcodec/metadata/vtx.c | 150 |
16 files changed, 1625 insertions, 22 deletions
diff --git a/lib/rbcodec/SOURCES b/lib/rbcodec/SOURCES index c288bb6de4..b0efebc989 100644 --- a/lib/rbcodec/SOURCES +++ b/lib/rbcodec/SOURCES @@ -40,6 +40,9 @@ metadata/asap.c metadata/asf.c metadata/au.c metadata/ay.c +#ifdef HAVE_FPU +metadata/vtx.c +#endif metadata/flac.c metadata/gbs.c metadata/hes.c diff --git a/lib/rbcodec/codecs/SOURCES b/lib/rbcodec/codecs/SOURCES index f0787d267d..78b7b97498 100644 --- a/lib/rbcodec/codecs/SOURCES +++ b/lib/rbcodec/codecs/SOURCES @@ -34,6 +34,9 @@ wav64.c tta.c wmapro.c ay.c +#ifdef HAVE_FPU +vtx.c +#endif gbs.c hes.c nsf.c diff --git a/lib/rbcodec/codecs/codecs.make b/lib/rbcodec/codecs/codecs.make index 4602a56d2c..d9554a9802 100644 --- a/lib/rbcodec/codecs/codecs.make +++ b/lib/rbcodec/codecs/codecs.make @@ -39,6 +39,7 @@ include $(RBCODECLIB_DIR)/codecs/liba52/liba52.make include $(RBCODECLIB_DIR)/codecs/libalac/libalac.make include $(RBCODECLIB_DIR)/codecs/libasap/libasap.make include $(RBCODECLIB_DIR)/codecs/libasf/libasf.make +include $(RBCODECLIB_DIR)/codecs/libayumi/libayumi.make include $(RBCODECLIB_DIR)/codecs/libfaad/libfaad.make include $(RBCODECLIB_DIR)/codecs/libffmpegFLAC/libffmpegFLAC.make include $(RBCODECLIB_DIR)/codecs/libm4a/libm4a.make @@ -74,6 +75,7 @@ $(ASAPLIB) : CODECFLAGS += -O1 $(ASFLIB) : CODECFLAGS += -O2 $(ATRACLIB) : CODECFLAGS += -O1 $(AYLIB) : CODECFLAGS += -O2 +$(AYUMILIB) : CODECFLAGS += -O3 $(COOKLIB): CODECFLAGS += -O1 $(DEMACLIB) : CODECFLAGS += -O3 $(FAADLIB) : CODECFLAGS += -O2 @@ -181,6 +183,7 @@ $(CODECDIR)/vox.codec : $(CODECDIR)/libpcm.a $(CODECDIR)/wav64.codec : $(CODECDIR)/libpcm.a $(CODECDIR)/tta.codec : $(CODECDIR)/libtta.a $(CODECDIR)/ay.codec : $(CODECDIR)/libay.a +$(CODECDIR)/vtx.codec : $(CODECDIR)/libayumi.a $(CODECDIR)/gbs.codec : $(CODECDIR)/libgbs.a $(CODECDIR)/hes.codec : $(CODECDIR)/libhes.a $(CODECDIR)/nsf.codec : $(CODECDIR)/libnsf.a $(CODECDIR)/libemu2413.a diff --git a/lib/rbcodec/codecs/libayumi/SOURCES b/lib/rbcodec/codecs/libayumi/SOURCES new file mode 100644 index 0000000000..075619bed1 --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/SOURCES @@ -0,0 +1,5 @@ +#ifdef HAVE_FPU +ayumi_render.c +ayumi.c +lzh.c +#endif diff --git a/lib/rbcodec/codecs/libayumi/ayumi.c b/lib/rbcodec/codecs/libayumi/ayumi.c new file mode 100644 index 0000000000..2e0419f6cb --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/ayumi.c @@ -0,0 +1,361 @@ +/* Author: Peter Sovietov */ + +#include <string.h> +#include <math.h> +#include "ayumi.h" + +static const double AY_dac_table[] = { + 0.0, 0.0, + 0.00999465934234, 0.00999465934234, + 0.0144502937362, 0.0144502937362, + 0.0210574502174, 0.0210574502174, + 0.0307011520562, 0.0307011520562, + 0.0455481803616, 0.0455481803616, + 0.0644998855573, 0.0644998855573, + 0.107362478065, 0.107362478065, + 0.126588845655, 0.126588845655, + 0.20498970016, 0.20498970016, + 0.292210269322, 0.292210269322, + 0.372838941024, 0.372838941024, + 0.492530708782, 0.492530708782, + 0.635324635691, 0.635324635691, + 0.805584802014, 0.805584802014, + 1.0, 1.0 +}; + +static const double YM_dac_table[] = { + 0.0, 0.0, + 0.00465400167849, 0.00772106507973, + 0.0109559777218, 0.0139620050355, + 0.0169985503929, 0.0200198367285, + 0.024368657969, 0.029694056611, + 0.0350652323186, 0.0403906309606, + 0.0485389486534, 0.0583352407111, + 0.0680552376593, 0.0777752346075, + 0.0925154497597, 0.111085679408, + 0.129747463188, 0.148485542077, + 0.17666895552, 0.211551079576, + 0.246387426566, 0.281101701381, + 0.333730067903, 0.400427252613, + 0.467383840696, 0.53443198291, + 0.635172045472, 0.75800717174, + 0.879926756695, 1.0 +}; + +static void reset_segment(struct ayumi* ay); + +double sqrt(double n) +{ + double x0 = n; + double x1; + for (;;) { + x1 = x0 - (x0 * x0 - n) / (2 * x0); + double delta = (x1 - x0) / x0; + if (delta < .000001 && delta > -.000001) + return x1; + x0 = x1; + } +} + +static int update_tone(struct ayumi* ay, int index) { + struct tone_channel* ch = &ay->channels[index]; + ch->tone_counter += 1; + if (ch->tone_counter >= ch->tone_period) { + ch->tone_counter = 0; + ch->tone ^= 1; + } + return ch->tone; +} + +static int update_noise(struct ayumi* ay) { + int bit0x3; + ay->noise_counter += 1; + if (ay->noise_counter >= (ay->noise_period << 1)) { + ay->noise_counter = 0; + bit0x3 = ((ay->noise ^ (ay->noise >> 3)) & 1); + ay->noise = (ay->noise >> 1) | (bit0x3 << 16); + } + return ay->noise & 1; +} + +static void slide_up(struct ayumi* ay) { + ay->envelope += 1; + if (ay->envelope > 31) { + ay->envelope_segment ^= 1; + reset_segment(ay); + } +} + +static void slide_down(struct ayumi* ay) { + ay->envelope -= 1; + if (ay->envelope < 0) { + ay->envelope_segment ^= 1; + reset_segment(ay); + } +} + +static void hold_top(struct ayumi* ay) { + (void) ay; +} + +static void hold_bottom(struct ayumi* ay) { + (void) ay; +} + +static void (* const Envelopes[][2])(struct ayumi*) = { + {slide_down, hold_bottom}, + {slide_down, hold_bottom}, + {slide_down, hold_bottom}, + {slide_down, hold_bottom}, + {slide_up, hold_bottom}, + {slide_up, hold_bottom}, + {slide_up, hold_bottom}, + {slide_up, hold_bottom}, + {slide_down, slide_down}, + {slide_down, hold_bottom}, + {slide_down, slide_up}, + {slide_down, hold_top}, + {slide_up, slide_up}, + {slide_up, hold_top}, + {slide_up, slide_down}, + {slide_up, hold_bottom} +}; + +static void reset_segment(struct ayumi* ay) { + if (Envelopes[ay->envelope_shape][ay->envelope_segment] == slide_down + || Envelopes[ay->envelope_shape][ay->envelope_segment] == hold_top) { + ay->envelope = 31; + return; + } + ay->envelope = 0; +} + +int update_envelope(struct ayumi* ay) { + ay->envelope_counter += 1; + if (ay->envelope_counter >= ay->envelope_period) { + ay->envelope_counter = 0; + Envelopes[ay->envelope_shape][ay->envelope_segment](ay); + } + return ay->envelope; +} + +static void update_mixer(struct ayumi* ay) { + int i; + int out; + int noise = update_noise(ay); + int envelope = update_envelope(ay); + ay->left = 0; + ay->right = 0; + for (i = 0; i < TONE_CHANNELS; i += 1) { + out = (update_tone(ay, i) | ay->channels[i].t_off) & (noise | ay->channels[i].n_off); + out *= ay->channels[i].e_on ? envelope : ay->channels[i].volume * 2 + 1; + ay->left += ay->dac_table[out] * ay->channels[i].pan_left; + ay->right += ay->dac_table[out] * ay->channels[i].pan_right; + } +} + +int ayumi_configure(struct ayumi* ay, int is_ym, double clock_rate, int sr) { + int i; + memset(ay, 0, sizeof(struct ayumi)); + ay->step = clock_rate / (sr * 8 * DECIMATE_FACTOR); + ay->dac_table = is_ym ? YM_dac_table : AY_dac_table; + ay->noise = 1; + ayumi_set_envelope(ay, 1); + for (i = 0; i < TONE_CHANNELS; i += 1) { + ayumi_set_tone(ay, i, 1); + } + return ay->step < 1; +} + +void ayumi_set_pan(struct ayumi* ay, int index, double pan, int is_eqp) { + if (is_eqp) { + ay->channels[index].pan_left = sqrt(1 - pan); + ay->channels[index].pan_right = sqrt(pan); + } else { + ay->channels[index].pan_left = 1 - pan; + ay->channels[index].pan_right = pan; + } +} + +void ayumi_set_tone(struct ayumi* ay, int index, int period) { + period &= 0xfff; + ay->channels[index].tone_period = (period == 0) | period; +} + +void ayumi_set_noise(struct ayumi* ay, int period) { + ay->noise_period = period & 0x1f; +} + +void ayumi_set_mixer(struct ayumi* ay, int index, int t_off, int n_off, int e_on) { + ay->channels[index].t_off = t_off & 1; + ay->channels[index].n_off = n_off & 1; + ay->channels[index].e_on = e_on; +} + +void ayumi_set_volume(struct ayumi* ay, int index, int volume) { + ay->channels[index].volume = volume & 0xf; +} + +void ayumi_set_envelope(struct ayumi* ay, int period) { + period &= 0xffff; + ay->envelope_period = (period == 0) | period; +} + +void ayumi_set_envelope_shape(struct ayumi* ay, int shape) { + ay->envelope_shape = shape & 0xf; + ay->envelope_counter = 0; + ay->envelope_segment = 0; + reset_segment(ay); +} + +static double decimate(double* x) { + double y = -0.0000046183113992051936 * (x[1] + x[191]) + + -0.00001117761640887225 * (x[2] + x[190]) + + -0.000018610264502005432 * (x[3] + x[189]) + + -0.000025134586135631012 * (x[4] + x[188]) + + -0.000028494281690666197 * (x[5] + x[187]) + + -0.000026396828793275159 * (x[6] + x[186]) + + -0.000017094212558802156 * (x[7] + x[185]) + + 0.000023798193576966866 * (x[9] + x[183]) + + 0.000051281160242202183 * (x[10] + x[182]) + + 0.00007762197826243427 * (x[11] + x[181]) + + 0.000096759426664120416 * (x[12] + x[180]) + + 0.00010240229300393402 * (x[13] + x[179]) + + 0.000089344614218077106 * (x[14] + x[178]) + + 0.000054875700118949183 * (x[15] + x[177]) + + -0.000069839082210680165 * (x[17] + x[175]) + + -0.0001447966132360757 * (x[18] + x[174]) + + -0.00021158452917708308 * (x[19] + x[173]) + + -0.00025535069106550544 * (x[20] + x[172]) + + -0.00026228714374322104 * (x[21] + x[171]) + + -0.00022258805927027799 * (x[22] + x[170]) + + -0.00013323230495695704 * (x[23] + x[169]) + + 0.00016182578767055206 * (x[25] + x[167]) + + 0.00032846175385096581 * (x[26] + x[166]) + + 0.00047045611576184863 * (x[27] + x[165]) + + 0.00055713851457530944 * (x[28] + x[164]) + + 0.00056212565121518726 * (x[29] + x[163]) + + 0.00046901918553962478 * (x[30] + x[162]) + + 0.00027624866838952986 * (x[31] + x[161]) + + -0.00032564179486838622 * (x[33] + x[159]) + + -0.00065182310286710388 * (x[34] + x[158]) + + -0.00092127787309319298 * (x[35] + x[157]) + + -0.0010772534348943575 * (x[36] + x[156]) + + -0.0010737727700273478 * (x[37] + x[155]) + + -0.00088556645390392634 * (x[38] + x[154]) + + -0.00051581896090765534 * (x[39] + x[153]) + + 0.00059548767193795277 * (x[41] + x[151]) + + 0.0011803558710661009 * (x[42] + x[150]) + + 0.0016527320270369871 * (x[43] + x[149]) + + 0.0019152679330965555 * (x[44] + x[148]) + + 0.0018927324805381538 * (x[45] + x[147]) + + 0.0015481870327877937 * (x[46] + x[146]) + + 0.00089470695834941306 * (x[47] + x[145]) + + -0.0010178225878206125 * (x[49] + x[143]) + + -0.0020037400552054292 * (x[50] + x[142]) + + -0.0027874356824117317 * (x[51] + x[141]) + + -0.003210329988021943 * (x[52] + x[140]) + + -0.0031540624117984395 * (x[53] + x[139]) + + -0.0025657163651900345 * (x[54] + x[138]) + + -0.0014750752642111449 * (x[55] + x[137]) + + 0.0016624165446378462 * (x[57] + x[135]) + + 0.0032591192839069179 * (x[58] + x[134]) + + 0.0045165685815867747 * (x[59] + x[133]) + + 0.0051838984346123896 * (x[60] + x[132]) + + 0.0050774264697459933 * (x[61] + x[131]) + + 0.0041192521414141585 * (x[62] + x[130]) + + 0.0023628575417966491 * (x[63] + x[129]) + + -0.0026543507866759182 * (x[65] + x[127]) + + -0.0051990251084333425 * (x[66] + x[126]) + + -0.0072020238234656924 * (x[67] + x[125]) + + -0.0082672928192007358 * (x[68] + x[124]) + + -0.0081033739572956287 * (x[69] + x[123]) + + -0.006583111539570221 * (x[70] + x[122]) + + -0.0037839040415292386 * (x[71] + x[121]) + + 0.0042781252851152507 * (x[73] + x[119]) + + 0.0084176358598320178 * (x[74] + x[118]) + + 0.01172566057463055 * (x[75] + x[117]) + + 0.013550476647788672 * (x[76] + x[116]) + + 0.013388189369997496 * (x[77] + x[115]) + + 0.010979501242341259 * (x[78] + x[114]) + + 0.006381274941685413 * (x[79] + x[113]) + + -0.007421229604153888 * (x[81] + x[111]) + + -0.01486456304340213 * (x[82] + x[110]) + + -0.021143584622178104 * (x[83] + x[109]) + + -0.02504275058758609 * (x[84] + x[108]) + + -0.025473530942547201 * (x[85] + x[107]) + + -0.021627310017882196 * (x[86] + x[106]) + + -0.013104323383225543 * (x[87] + x[105]) + + 0.017065133989980476 * (x[89] + x[103]) + + 0.036978919264451952 * (x[90] + x[102]) + + 0.05823318062093958 * (x[91] + x[101]) + + 0.079072012081405949 * (x[92] + x[100]) + + 0.097675998716952317 * (x[93] + x[99]) + + 0.11236045936950932 * (x[94] + x[98]) + + 0.12176343577287731 * (x[95] + x[97]) + + 0.125 * x[96]; + memcpy(&x[FIR_SIZE - DECIMATE_FACTOR], x, DECIMATE_FACTOR * sizeof(double)); + return y; +} + +void ayumi_process(struct ayumi* ay) { + int i; + double y1; + double* c_left = ay->interpolator_left.c; + double* y_left = ay->interpolator_left.y; + double* c_right = ay->interpolator_right.c; + double* y_right = ay->interpolator_right.y; + double* fir_left = &ay->fir_left[FIR_SIZE - ay->fir_index * DECIMATE_FACTOR]; + double* fir_right = &ay->fir_right[FIR_SIZE - ay->fir_index * DECIMATE_FACTOR]; + ay->fir_index = (ay->fir_index + 1) % (FIR_SIZE / DECIMATE_FACTOR - 1); + for (i = DECIMATE_FACTOR - 1; i >= 0; i -= 1) { + ay->x += ay->step; + if (ay->x >= 1) { + ay->x -= 1; + y_left[0] = y_left[1]; + y_left[1] = y_left[2]; + y_left[2] = y_left[3]; + y_right[0] = y_right[1]; + y_right[1] = y_right[2]; + y_right[2] = y_right[3]; + update_mixer(ay); + y_left[3] = ay->left; + y_right[3] = ay->right; + y1 = y_left[2] - y_left[0]; + c_left[0] = 0.5 * y_left[1] + 0.25 * (y_left[0] + y_left[2]); + c_left[1] = 0.5 * y1; + c_left[2] = 0.25 * (y_left[3] - y_left[1] - y1); + y1 = y_right[2] - y_right[0]; + c_right[0] = 0.5 * y_right[1] + 0.25 * (y_right[0] + y_right[2]); + c_right[1] = 0.5 * y1; + c_right[2] = 0.25 * (y_right[3] - y_right[1] - y1); + } + fir_left[i] = (c_left[2] * ay->x + c_left[1]) * ay->x + c_left[0]; + fir_right[i] = (c_right[2] * ay->x + c_right[1]) * ay->x + c_right[0]; + } + ay->left = decimate(fir_left); + ay->right = decimate(fir_right); +} + +void ayumi_seek(struct ayumi* ay) { + int i; + for (i = DECIMATE_FACTOR - 1; i >= 0; i -= 1) { + ay->x += ay->step; + if (ay->x >= 1) { + ay->x -= 1; + update_mixer(ay); + } + } +} + +static double dc_filter(struct dc_filter* dc, int index, double x) { + dc->sum += -dc->delay[index] + x; + dc->delay[index] = x; + return x - dc->sum / DC_FILTER_SIZE; +} + +void ayumi_remove_dc(struct ayumi* ay) { + ay->left = dc_filter(&ay->dc_left, ay->dc_index, ay->left); + ay->right = dc_filter(&ay->dc_right, ay->dc_index, ay->right); + ay->dc_index = (ay->dc_index + 1) & (DC_FILTER_SIZE - 1); +} diff --git a/lib/rbcodec/codecs/libayumi/ayumi.h b/lib/rbcodec/codecs/libayumi/ayumi.h new file mode 100644 index 0000000000..66d767797e --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/ayumi.h @@ -0,0 +1,72 @@ +/* Author: Peter Sovietov */ + +#ifndef AYUMI_H +#define AYUMI_H + +enum { + TONE_CHANNELS = 3, + DECIMATE_FACTOR = 8, + FIR_SIZE = 192, + DC_FILTER_SIZE = 1024 +}; + +struct tone_channel { + int tone_period; + int tone_counter; + int tone; + int t_off; + int n_off; + int e_on; + int volume; + double pan_left; + double pan_right; +}; + +struct interpolator { + double c[4]; + double y[4]; +}; + +struct dc_filter { + double sum; + double delay[DC_FILTER_SIZE]; +}; + +struct ayumi { + struct tone_channel channels[TONE_CHANNELS]; + int noise_period; + int noise_counter; + int noise; + int envelope_counter; + int envelope_period; + int envelope_shape; + int envelope_segment; + int envelope; + const double* dac_table; + double step; + double x; + struct interpolator interpolator_left; + struct interpolator interpolator_right; + double fir_left[FIR_SIZE * 2]; + double fir_right[FIR_SIZE * 2]; + int fir_index; + struct dc_filter dc_left; + struct dc_filter dc_right; + int dc_index; + double left; + double right; +}; + +int ayumi_configure(struct ayumi* ay, int is_ym, double clock_rate, int sr); +void ayumi_set_pan(struct ayumi* ay, int index, double pan, int is_eqp); +void ayumi_set_tone(struct ayumi* ay, int index, int period); +void ayumi_set_noise(struct ayumi* ay, int period); +void ayumi_set_mixer(struct ayumi* ay, int index, int t_off, int n_off, int e_on); +void ayumi_set_volume(struct ayumi* ay, int index, int volume); +void ayumi_set_envelope(struct ayumi* ay, int period); +void ayumi_set_envelope_shape(struct ayumi* ay, int shape); +void ayumi_process(struct ayumi* ay); +void ayumi_seek(struct ayumi* ay); +void ayumi_remove_dc(struct ayumi* ay); + +#endif diff --git a/lib/rbcodec/codecs/libayumi/ayumi_render.c b/lib/rbcodec/codecs/libayumi/ayumi_render.c new file mode 100644 index 0000000000..9bf0204597 --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/ayumi_render.c @@ -0,0 +1,328 @@ +#include "ayumi_render.h" + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include "ayumi.h" +#include "lzh.h" +#include "codeclib.h" + +ayumi_render_t ay; + +/* default panning settings, 7 stereo types */ +static const double default_pan[7][3] = { +/* A, B, C */ + + {0.50, 0.50, 0.50}, /* MONO */ + {0.10, 0.50, 0.90}, /* ABC */ + {0.10, 0.90, 0.50}, /* ACB */ + {0.50, 0.10, 0.90}, /* BAC */ + {0.90, 0.10, 0.50}, /* BCA */ + {0.50, 0.90, 0.10}, /* CAB */ + {0.90, 0.50, 0.10} /* CBA */ +}; + +static const char *chiptype_name[3] = { + "AY-3-8910", + "YM2149", + "Unknown" +}; + +static const char *layout_name[9] = { + "Mono", + "ABC Stereo", + "ACB Stereo", + "BAC Stereo", + "BCA Stereo", + "CAB Stereo", + "CBA Stereo", + "Custom", + "Unknown" +}; + +/* reader */ + +#define VTX_STRING_MAX 254 + +typedef struct { + uchar *ptr; + uint size; +} reader_t; + +reader_t reader; + +void Reader_Init(void *pBlock) { + reader.ptr = (uchar *) pBlock; + reader.size = 0; +} + +uint Reader_ReadByte(void) { + uint res; + res = *reader.ptr++; + reader.size += 1; + return res; +} + +uint Reader_ReadWord(void) { + uint res; + res = *reader.ptr++; + res += *reader.ptr++ << 8; + reader.size += 2; + return res; +} + +uint Reader_ReadDWord(void) { + uint res; + res = *reader.ptr++; + res += *reader.ptr++ << 8; + res += *reader.ptr++ << 16; + res += *reader.ptr++ << 24; + reader.size += 4; + return res; +} + +char *Reader_ReadString(void) { + char *res; + if (reader.ptr == NULL) + return NULL; + int len = strlen((const char *)reader.ptr); + if (len > VTX_STRING_MAX) + return NULL; + res = reader.ptr; + reader.ptr += len + 1; + reader.size += len + 1; + return res; +} + +uchar *Reader_GetPtr(void) { + return reader.ptr; +} + +uint Reader_GetSize(void) { + return reader.size; +} + +/* ayumi_render */ + +static int AyumiRender_LoadInfo(void *pBlock, uint size) +{ + if (size < 20) + return 0; + + Reader_Init(pBlock); + + uint hdr = Reader_ReadWord(); + + if (hdr == 0x7961) + ay.info.chiptype = VTX_CHIP_AY; + else if (hdr == 0x6d79) + ay.info.chiptype = VTX_CHIP_YM; + else { + return 0; + } + + ay.info.layout = (vtx_layout_t) + Reader_ReadByte(); + ay.info.loop = Reader_ReadWord(); + ay.info.chipfreq = Reader_ReadDWord(); + ay.info.playerfreq = Reader_ReadByte(); + ay.info.year = Reader_ReadWord(); + ay.data.regdata_size = Reader_ReadDWord(); + ay.info.frames = ay.data.regdata_size / 14; + ay.info.title = Reader_ReadString(); + ay.info.author = Reader_ReadString(); + ay.info.from = Reader_ReadString(); + ay.info.tracker = Reader_ReadString(); + ay.info.comment = Reader_ReadString(); + + ay.data.lzhdata_size = size - Reader_GetSize(); + ay.data.lzhdata = (uchar *)codec_malloc(ay.data.lzhdata_size); + memcpy(ay.data.lzhdata, Reader_GetPtr(), ay.data.lzhdata_size); + + return 1; +} + +int AyumiRender_LoadFile(void *pBlock, uint size) +{ + if (!AyumiRender_LoadInfo(pBlock, size)) + return 0; + + ay.data.regdata = (uchar *)codec_malloc(ay.data.regdata_size); + if (ay.data.regdata == NULL) + return 0; + + int bRet = LzUnpack(ay.data.lzhdata, ay.data.lzhdata_size, + ay.data.regdata, ay.data.regdata_size); + + if (bRet) + return 0; + + return 1; +} + +const char *AyumiRender_GetChipTypeName(vtx_chiptype_t chiptype) +{ + if (chiptype > VTX_CHIP_YM) + chiptype = (vtx_chiptype_t) (VTX_CHIP_YM + 1); + return chiptype_name[chiptype]; +} + +const char *AyumiRender_GetLayoutName(vtx_layout_t layout) +{ + if (layout > VTX_LAYOUT_CUSTOM) + layout = (vtx_layout_t) (VTX_LAYOUT_CUSTOM + 1); + return layout_name[layout]; +} + +int AyumiRender_AyInit(vtx_chiptype_t chiptype, uint samplerate, + uint chipfreq, double playerfreq, uint dcfilter) +{ + if (chiptype > VTX_CHIP_YM) + return 0; + if ((samplerate < 8000) || (samplerate > 768000)) + return 0; + if ((chipfreq < 1000000) || (chipfreq > 2000000)) + return 0; + if ((playerfreq < 1) || (playerfreq > 100)) + return 0; + + ay.is_ym = (chiptype == VTX_CHIP_YM) ? 1 : 0; + ay.clock_rate = chipfreq; + ay.sr = samplerate; + + ay.dc_filter_on = dcfilter ? 1 : 0; + + ay.frame = 0; + ay.isr_counter = 1; + ay.isr_step = playerfreq / samplerate; + + if (!ayumi_configure(&ay.ay, ay.is_ym, ay.clock_rate, ay.sr)) + return 0; + + return 1; +} + +int AyumiRender_SetLayout(vtx_layout_t layout, uint eqpower) +{ + if (layout > VTX_LAYOUT_CUSTOM) + return 0; + ay.is_eqp = eqpower ? 1 : 0; + + switch (layout) { + case VTX_LAYOUT_MONO: + case VTX_LAYOUT_ABC: + case VTX_LAYOUT_ACB: + case VTX_LAYOUT_BAC: + case VTX_LAYOUT_BCA: + case VTX_LAYOUT_CAB: + case VTX_LAYOUT_CBA: + for (int i = 0; i < 3; i++) + ay.pan[i] = default_pan[layout][i]; + break; + case VTX_LAYOUT_CUSTOM: + for (int i = 0; i < 3; i++) + ay.pan[i] = 0; // no custom layout + break; + default: + return 0; + } + + for (int i = 0; i < 3; i++) + ayumi_set_pan(&ay.ay, i, ay.pan[i], ay.is_eqp); + + return 1; +} + +uint AyumiRender_GetPos(void) +{ + return ay.frame; +} + +uint AyumiRender_GetMaxPos(void) +{ + return ay.info.frames; +} + +static void AyumiRender_UpdateAyumiState(void) +{ + int r[16]; + + if (ay.frame < ay.info.frames) { + uchar *ptr = ay.data.regdata + ay.frame; + for (int n = 0; n < 14; n++) { + r[n] = *ptr; + ptr += ay.info.frames; + } + } else { + for (int n = 0; n < 14; n++) { + r[n] = 0; + } + } + + ayumi_set_tone(&ay.ay, 0, (r[1] << 8) | r[0]); + ayumi_set_tone(&ay.ay, 1, (r[3] << 8) | r[2]); + ayumi_set_tone(&ay.ay, 2, (r[5] << 8) | r[4]); + ayumi_set_noise(&ay.ay, r[6]); + ayumi_set_mixer(&ay.ay, 0, r[7] & 1, (r[7] >> 3) & 1, r[8] >> 4); + ayumi_set_mixer(&ay.ay, 1, (r[7] >> 1) & 1, (r[7] >> 4) & 1, r[9] >> 4); + ayumi_set_mixer(&ay.ay, 2, (r[7] >> 2) & 1, (r[7] >> 5) & 1, r[10] >> 4); + ayumi_set_volume(&ay.ay, 0, r[8] & 0xf); + ayumi_set_volume(&ay.ay, 1, r[9] & 0xf); + ayumi_set_volume(&ay.ay, 2, r[10] & 0xf); + ayumi_set_envelope(&ay.ay, (r[12] << 8) | r[11]); + if (r[13] != 255) { + ayumi_set_envelope_shape(&ay.ay, r[13]); + } +} + +int AyumiRender_Seek(ulong nSample) +{ + ulong samples = 0; + + ay.frame = 0; + ay.isr_counter = 1; + + ayumi_configure(&ay.ay, ay.is_ym, ay.clock_rate, ay.sr); + + for (int i = 0; i < 3; i++) + ayumi_set_pan(&ay.ay, i, ay.pan[i], ay.is_eqp); + + while (samples < nSample) { + ay.isr_counter += ay.isr_step; + if (ay.isr_counter >= 1) { + ay.isr_counter -= 1; + AyumiRender_UpdateAyumiState(); + ay.frame += 1; + } + ayumi_seek(&ay.ay); + samples++; + } + + return 1; +} + +ulong AyumiRender_AySynth(void *pBuffer, ulong nSamples) +{ + ulong samples = 0; + short *out = (int16_t *) pBuffer; + + for (ulong i = 0; i < nSamples; i++) { + ay.isr_counter += ay.isr_step; + if (ay.isr_counter >= 1) { + ay.isr_counter -= 1; + AyumiRender_UpdateAyumiState(); + ay.frame += 1; + } + ayumi_process(&ay.ay); + if (ay.dc_filter_on) { + ayumi_remove_dc(&ay.ay); + } + out[0] = (int16_t)(ay.ay.left * 16383); + out[1] = (int16_t)(ay.ay.right * 16383); + out += 2; + samples++; + } + + return samples; +} diff --git a/lib/rbcodec/codecs/libayumi/ayumi_render.h b/lib/rbcodec/codecs/libayumi/ayumi_render.h new file mode 100644 index 0000000000..b09ea9fb3d --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/ayumi_render.h @@ -0,0 +1,84 @@ +#ifndef AYUMI_RENDER_H +#define AYUMI_RENDER_H + +#include "ayumi.h" + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef enum { + VTX_CHIP_AY = 0, /* emulate AY */ + VTX_CHIP_YM /* emulate YM */ +} vtx_chiptype_t; + +typedef enum { + VTX_LAYOUT_MONO = 0, + VTX_LAYOUT_ABC, + VTX_LAYOUT_ACB, + VTX_LAYOUT_BAC, + VTX_LAYOUT_BCA, + VTX_LAYOUT_CAB, + VTX_LAYOUT_CBA, + VTX_LAYOUT_CUSTOM +} vtx_layout_t; + +typedef struct { + vtx_chiptype_t chiptype; /* Type of sound chip */ + vtx_layout_t layout; /* stereo layout */ + uint loop; /* song loop */ + uint chipfreq; /* AY chip freq (1773400 for ZX) */ + uint playerfreq; /* 50 Hz for ZX, 60 Hz for yamaha */ + uint year; /* year song composed */ + char *title; /* song title */ + char *author; /* song author */ + char *from; /* song from */ + char *tracker; /* tracker */ + char *comment; /* comment */ + uint frames; /* number of AY data frames */ +} vtx_info_t; + +typedef struct { + uchar *lzhdata; /* packed song data */ + uint lzhdata_size; /* size of packed data */ + uchar *regdata; /* unpacked song data */ + uint regdata_size; /* size of unpacked data */ +} vtx_data_t; + +typedef struct { + uint frame; /* current frame position */ + double isr_step; + double isr_counter; + + int dc_filter_on; + + int is_ym; + double clock_rate; + int sr; + + double pan[3]; + int is_eqp; + + struct ayumi ay; /* ayumi structure */ + vtx_data_t data; /* packed & unpacked vtx data */ + vtx_info_t info; /* vtx info */ +} ayumi_render_t; + +int AyumiRender_LoadFile(void *pBlock, uint size); + +const char *AyumiRender_GetChipTypeName(vtx_chiptype_t chiptype); +const char *AyumiRender_GetLayoutName(vtx_layout_t layout); + +uint AyumiRender_GetPos(void); +uint AyumiRender_GetMaxPos(void); + +int AyumiRender_AyInit(vtx_chiptype_t chiptype, uint samplerate, uint chipfreq, + double playerfreq, uint dcfilter); +int AyumiRender_SetLayout(vtx_layout_t layout, uint eqpower); + +int AyumiRender_Seek(ulong nSample); + +ulong AyumiRender_AySynth(void *pBuffer, ulong nSamples); + +#endif /* ifndef AYUMI_RENDER_H */ diff --git a/lib/rbcodec/codecs/libayumi/libayumi.make b/lib/rbcodec/codecs/libayumi/libayumi.make new file mode 100644 index 0000000000..029aa11a6f --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/libayumi.make @@ -0,0 +1,18 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +# libayumi +AYUMILIB := $(CODECDIR)/libayumi.a +AYUMILIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/libayumi/SOURCES) +AYUMILIB_OBJ := $(call c2obj, $(AYUMILIB_SRC)) +OTHER_SRC += $(AYUMILIB_SRC) + +$(AYUMILIB): $(AYUMILIB_OBJ) + $(SILENT)$(shell rm -f $@) + $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null diff --git a/lib/rbcodec/codecs/libayumi/lzh.c b/lib/rbcodec/codecs/libayumi/lzh.c new file mode 100644 index 0000000000..786d3bbafe --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/lzh.c @@ -0,0 +1,420 @@ +#include "lzh.h" + +#include <string.h> + +#define BUFSIZE (1024 * 4) + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#ifndef UCHAR_MAX +#define UCHAR_MAX 255 +#endif + +typedef ushort BITBUFTYPE; + +#define BITBUFSIZ (CHAR_BIT * sizeof(BITBUFTYPE)) +#define DICBIT 13 /* 12(-lh4-) or 13(-lh5-) */ +#define DICSIZ (1U << DICBIT) +#define MAXMATCH 256 /* formerly F (not more than UCHAR_MAX + 1) */ +#define THRESHOLD 3 /* choose optimal value */ +#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) /* alphabet = {0, 1, 2, ..., NC - 1} */ +#define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */ +#define CODE_BIT 16 /* codeword length */ + +#define MAX_HASH_VAL (3 * DICSIZ + (DICSIZ / 512 + 1) * UCHAR_MAX) + +#define NP (DICBIT + 1) +#define NT (CODE_BIT + 3) +#define PBIT 4 /* smallest integer such that (1U << PBIT) > NP */ +#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */ +#if NT > NP +#define NPT NT +#else +#define NPT NP +#endif + +uchar *m_pSrc; +int m_srcSize; +uchar *m_pDst; +int m_dstSize; + +int DataIn(void *pBuffer, int nBytes); +int DataOut(void *pOut, int nBytes); + +void fillbuf(int n); +ushort getbits(int n); +void init_getbits(void); +int make_table(int nchar, uchar *bitlen, int tablebits, ushort *table); +void read_pt_len(int nn, int nbit, int i_special); +void read_c_len(void); +ushort decode_c(void); +ushort decode_p(void); +void huf_decode_start(void); +void decode_start(void); +void decode(uint count, uchar buffer[]); + +int fillbufsize; +uchar buf[BUFSIZE]; +uchar outbuf[DICSIZ]; +ushort left[2 * NC - 1]; +ushort right[2 * NC - 1]; +BITBUFTYPE bitbuf; +uint subbitbuf; +int bitcount; +int decode_j; /* remaining bytes to copy */ +uchar c_len[NC]; +uchar pt_len[NPT]; +uint blocksize; +ushort c_table[4096]; +ushort pt_table[256]; +int with_error; + +uint fillbuf_i; /* NOTE: these ones are not initialized at constructor time but inside the fillbuf and decode func. */ +uint decode_i; + +/* Additions */ + +int DataIn(void *pBuffer, int nBytes) +{ + const int np = (nBytes <= m_srcSize) ? nBytes : m_srcSize; + if (np > 0) { + memcpy(pBuffer, m_pSrc, np); + m_pSrc += np; + m_srcSize -= np; + } + return np; +} + +int DataOut(void *pBuffer, int nBytes) +{ + const int np = (nBytes <= m_dstSize) ? nBytes : m_dstSize; + if (np > 0) { + memcpy(m_pDst, pBuffer, np); + m_pDst += np; + m_dstSize -= np; + } + return np; +} + +/* io.c */ + +/* Shift bitbuf n bits left, read n bits */ +void fillbuf(int n) +{ + bitbuf = (bitbuf << n) & 0xffff; + while (n > bitcount) { + bitbuf |= subbitbuf << (n -= bitcount); + if (fillbufsize == 0) { + fillbuf_i = 0; + fillbufsize = DataIn(buf, BUFSIZE - 32); + } + if (fillbufsize > 0) + fillbufsize--, subbitbuf = buf[fillbuf_i++]; + else + subbitbuf = 0; + bitcount = CHAR_BIT; + } + bitbuf |= subbitbuf >> (bitcount -= n); +} + +ushort getbits(int n) +{ + ushort x; + x = bitbuf >> (BITBUFSIZ - n); + fillbuf(n); + return x; +} + +void init_getbits(void) +{ + bitbuf = 0; + subbitbuf = 0; + bitcount = 0; + fillbuf(BITBUFSIZ); +} + +/* maketbl.c */ + +int make_table(int nchar, uchar * bitlen, int tablebits, ushort * table) +{ + ushort count[17], weight[17], start[18], *p; + uint jutbits, avail, mask; + int i, ch, len, nextcode; + + for (i = 1; i <= 16; i++) + count[i] = 0; + for (i = 0; i < nchar; i++) + count[bitlen[i]]++; + + start[1] = 0; + for (i = 1; i <= 16; i++) + start[i + 1] = start[i] + (count[i] << (16 - i)); + if (start[17] != (ushort) (1U << 16)) + return (1); /* error: bad table */ + + jutbits = 16 - tablebits; + for (i = 1; i <= tablebits; i++) { + start[i] >>= jutbits; + weight[i] = 1U << (tablebits - i); + } + while (i <= 16) { + weight[i] = 1U << (16 - i); + i++; + } + + i = start[tablebits + 1] >> jutbits; + if (i != (ushort) (1U << 16)) { + int k = 1U << tablebits; + while (i != k) + table[i++] = 0; + } + + avail = nchar; + mask = 1U << (15 - tablebits); + for (ch = 0; ch < nchar; ch++) { + if ((len = bitlen[ch]) == 0) + continue; + nextcode = start[len] + weight[len]; + if (len <= tablebits) { + for (i = start[len]; i < nextcode; i++) + table[i] = ch; + } else { + uint k = start[len]; + p = &table[k >> jutbits]; + i = len - tablebits; + while (i != 0) { + if (*p == 0) { + right[avail] = left[avail] = 0; + *p = avail++; + } + if (k & mask) + p = &right[*p]; + else + p = &left[*p]; + k <<= 1; + i--; + } + *p = ch; + } + start[len] = nextcode; + } + return (0); +} + +/* huf.c */ + +void read_pt_len(int nn, int nbit, int i_special) +{ + int i, n; + short c; + ushort mask; + + n = getbits(nbit); + if (n == 0) { + c = getbits(nbit); + for (i = 0; i < nn; i++) + pt_len[i] = 0; + for (i = 0; i < 256; i++) + pt_table[i] = c; + } else { + i = 0; + while (i < n) { + c = bitbuf >> (BITBUFSIZ - 3); + if (c == 7) { + mask = 1U << (BITBUFSIZ - 1 - 3); + while (mask & bitbuf) { + mask >>= 1; + c++; + } + } + fillbuf((c < 7) ? 3 : c - 3); + pt_len[i++] = (unsigned char) (c); + if (i == i_special) { + c = getbits(2); + while (--c >= 0) + pt_len[i++] = 0; + } + } + while (i < nn) + pt_len[i++] = 0; + make_table(nn, pt_len, 8, pt_table); + } +} + +void read_c_len(void) +{ + short i, c, n; + ushort mask; + + n = getbits(CBIT); + if (n == 0) { + c = getbits(CBIT); + for (i = 0; i < NC; i++) + c_len[i] = 0; + for (i = 0; i < 4096; i++) + c_table[i] = c; + } else { + i = 0; + while (i < n) { + c = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (c >= NT) { + mask = 1U << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) + c = right[c]; + else + c = left[c]; + mask >>= 1; + } while (c >= NT); + } + fillbuf(pt_len[c]); + if (c <= 2) { + if (c == 0) + c = 1; + else if (c == 1) + c = getbits(4) + 3; + else + c = getbits(CBIT) + 20; + while (--c >= 0) + c_len[i++] = 0; + } else + c_len[i++] = c - 2; + } + while (i < NC) + c_len[i++] = 0; + make_table(NC, c_len, 12, c_table); + } +} + +ushort decode_c(void) +{ + ushort j, mask; + + if (blocksize == 0) { + blocksize = getbits(16); + read_pt_len(NT, TBIT, 3); + read_c_len(); + read_pt_len(NP, PBIT, -1); + } + blocksize--; + j = c_table[bitbuf >> (BITBUFSIZ - 12)]; + if (j >= NC) { + mask = 1U << (BITBUFSIZ - 1 - 12); + do { + if (bitbuf & mask) + j = right[j]; + else + j = left[j]; + mask >>= 1; + } + while (j >= NC); + } + fillbuf(c_len[j]); + return j; +} + +ushort decode_p(void) +{ + ushort j, mask; + + j = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (j >= NP) { + mask = 1U << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) + j = right[j]; + else + j = left[j]; + mask >>= 1; + } while (j >= NP); + } + fillbuf(pt_len[j]); + if (j != 0) + j = (1U << (j - 1)) + getbits(j - 1); + return j; +} + +void huf_decode_start(void) +{ + init_getbits(); + blocksize = 0; +} + +/* decode.c */ + +void decode_start(void) +{ + fillbufsize = 0; + huf_decode_start(); + decode_j = 0; +} + +/* + * The calling function must keep the number of bytes to be processed. This + * function decodes either 'count' bytes or 'DICSIZ' bytes, whichever is + * smaller, into the array 'buffer[]' of size 'DICSIZ' or more. Call + * decode_start() once for each new file before calling this function. + */ +void decode(uint count, uchar buffer[]) +{ + uint r, c; + + r = 0; + while (--decode_j >= 0) { + buffer[r] = buffer[decode_i]; + decode_i = (decode_i + 1) & (DICSIZ - 1); + if (++r == count) + return; + } + for (;;) { + c = decode_c(); + if (c <= UCHAR_MAX) { + buffer[r] = c; + if (++r == count) + return; + } else { + decode_j = c - (UCHAR_MAX + 1 - THRESHOLD); + decode_i = (r - decode_p() - 1) & (DICSIZ - 1); + while (--decode_j >= 0) { + buffer[r] = buffer[decode_i]; + decode_i = (decode_i + 1) & (DICSIZ - 1); + if (++r == count) + return; + } + } + } +} + +int LzUnpack(void *pSrc, int srcSize, void *pDst, int dstSize) +{ + with_error = 0; + + m_pSrc = (uchar *) pSrc; + m_srcSize = srcSize; + m_pDst = (uchar *) pDst; + m_dstSize = dstSize; + + decode_start(); + + unsigned int origsize = dstSize; + while (origsize != 0) { + int n = (uint) ((origsize > DICSIZ) ? DICSIZ : origsize); + decode(n, outbuf); + if (with_error) + break; + + DataOut(outbuf, n); + origsize -= n; + if (with_error) + break; + } + + return (with_error); +} diff --git a/lib/rbcodec/codecs/libayumi/lzh.h b/lib/rbcodec/codecs/libayumi/lzh.h new file mode 100644 index 0000000000..5d896a56a3 --- /dev/null +++ b/lib/rbcodec/codecs/libayumi/lzh.h @@ -0,0 +1,6 @@ +#ifndef LZH_H +#define LZH_H + +int LzUnpack(void *pSrc, int srcSize, void *pDst, int dstSize); + +#endif /* ifndef LZH_H */ diff --git a/lib/rbcodec/codecs/vtx.c b/lib/rbcodec/codecs/vtx.c new file mode 100644 index 0000000000..031af946b9 --- /dev/null +++ b/lib/rbcodec/codecs/vtx.c @@ -0,0 +1,138 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * VTX Codec for rockbox based on the Ayumi engine + * + * Ayumi engine Written by Peter Sovietov in 2015 + * Ported to rockbox '2019 by Roman Stolyarov + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include <codecs/lib/codeclib.h> +#include "libayumi/ayumi_render.h" + +CODEC_HEADER + +#define VTX_SAMPLE_RATE 44100 + +/* Maximum number of bytes to process in one iteration */ +#define CHUNK_SIZE (1024*2) + +static int16_t samples[CHUNK_SIZE] IBSS_ATTR; +extern ayumi_render_t ay; + +/****************** rockbox interface ******************/ + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* we only render 16 bits */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 16); + + /* 44 Khz, Interleaved stereo */ + ci->configure(DSP_SET_FREQUENCY, VTX_SAMPLE_RATE); + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + uint8_t *buf; + size_t n; + intptr_t param; + uint32_t elapsed_time; + long smp; + int res; + + /* reset values */ + elapsed_time = 0; + param = ci->id3->elapsed; + + DEBUGF("VTX: next_track\n"); + if (codec_init()) { + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + /* Read the entire file */ + DEBUGF("VTX: request file\n"); + ci->seek_buffer(0); + buf = ci->request_buffer(&n, ci->filesize); + if (!buf || n < (size_t)ci->filesize) { + DEBUGF("VTX: file load failed\n"); + return CODEC_ERROR; + } + + res = AyumiRender_LoadFile((void *)buf, ci->filesize); + if (!res) { + DEBUGF("VTX: AyumiRender_LoadFile failed\n"); + return CODEC_ERROR; + } + + res = AyumiRender_AyInit(ay.info.chiptype, VTX_SAMPLE_RATE, ay.info.chipfreq, ay.info.playerfreq, 1); + if (!res) { + DEBUGF("VTX: AyumiRender_AyInit failed\n"); + return CODEC_ERROR; + } + + res = AyumiRender_SetLayout(ay.info.layout, 0); + if (!res) { + DEBUGF("VTX: AyumiRender_SetLayout failed\n"); + return CODEC_ERROR; + } + + if (param) { + goto resume_start; + } + + /* The main decoder loop */ + while (1) { + long action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + resume_start: + ci->set_elapsed(param); + elapsed_time = param; + ulong sample = ((ulong)elapsed_time * 441) / 10; + AyumiRender_Seek(sample); + ci->seek_complete(); + } + + /* Generate audio buffer */ + smp = AyumiRender_AySynth((void *)&samples[0], CHUNK_SIZE >> 1); + + ci->pcmbuf_insert(samples, NULL, smp); + + /* Set elapsed time for one track files */ + elapsed_time += smp * 10 / 441; + ci->set_elapsed(elapsed_time); + + if (AyumiRender_GetPos() >= AyumiRender_GetMaxPos()) + break; + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/metadata/metadata.c b/lib/rbcodec/metadata/metadata.c index 9c41347975..aec72db97f 100644 --- a/lib/rbcodec/metadata/metadata.c +++ b/lib/rbcodec/metadata/metadata.c @@ -43,7 +43,7 @@ static bool get_shn_metadata(int fd, struct mp3entry *id3) } static bool get_other_asap_metadata(int fd, struct mp3entry *id3) -{ +{ id3->bitrate = 706; id3->frequency = 44100; id3->vbr = false; @@ -85,7 +85,7 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = /* Musepack SV7 */ [AFMT_MPC_SV7] = AFMT_ENTRY("MPCv7", "mpc", NULL, get_musepack_metadata,"mpc\0"), - /* A/52 (aka AC3) audio */ + /* A/52 (aka AC3) audio */ [AFMT_A52] = AFMT_ENTRY("AC3", "a52", NULL, get_a52_metadata, "a52\0ac3\0"), /* WavPack */ @@ -109,7 +109,7 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = /* NESM (NES Sound Format) */ [AFMT_NSF] = AFMT_ENTRY("NSF", "nsf", NULL, get_nsf_metadata, "nsf\0nsfe\0"), - /* Speex File Format */ + /* Speex File Format */ [AFMT_SPEEX] = AFMT_ENTRY("Speex", "speex",NULL, get_ogg_metadata, "spx\0"), /* SPC700 Save State */ @@ -162,12 +162,12 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = AFMT_ENTRY("DLT", "asap", NULL, get_other_asap_metadata,"dlt\0"), /* Atari MPT File */ [AFMT_MPT] = - AFMT_ENTRY("MPT", "asap", NULL, get_other_asap_metadata,"mpt\0"), + AFMT_ENTRY("MPT", "asap", NULL, get_other_asap_metadata,"mpt\0"), /* Atari MPD File */ [AFMT_MPD] = AFMT_ENTRY("MPD", "asap", NULL, get_other_asap_metadata,"mpd\0"), /* Atari RMT File */ - [AFMT_RMT] = + [AFMT_RMT] = AFMT_ENTRY("RMT", "asap", NULL, get_other_asap_metadata,"rmt\0"), /* Atari TMC File */ [AFMT_TMC] = @@ -177,10 +177,10 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = AFMT_ENTRY("TM8", "asap", NULL, get_other_asap_metadata,"tm8\0"), /* Atari TM2 File */ [AFMT_TM2] = - AFMT_ENTRY("TM2", "asap", NULL, get_other_asap_metadata,"tm2\0"), + AFMT_ENTRY("TM2", "asap", NULL, get_other_asap_metadata,"tm2\0"), /* Atrac3 in Sony OMA Container */ [AFMT_OMA_ATRAC3] = - AFMT_ENTRY("ATRAC3","atrac3_oma",NULL, get_oma_metadata, "oma\0aa3\0"), + AFMT_ENTRY("ATRAC3","atrac3_oma",NULL, get_oma_metadata, "oma\0aa3\0"), /* SMAF (Synthetic music Mobile Application Format) */ [AFMT_SMAF] = AFMT_ENTRY("SMAF", "smaf", NULL, get_smaf_metadata, "mmf\0"), @@ -194,7 +194,7 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = [AFMT_WAVE64] = AFMT_ENTRY("WAVE64","wav64",NULL, get_wave64_metadata,"w64\0"), /* True Audio */ - [AFMT_TTA] = + [AFMT_TTA] = AFMT_ENTRY("TTA", "tta", NULL, get_tta_metadata, "tta\0"), /* WMA Voice in ASF */ [AFMT_WMAVOICE] = @@ -206,8 +206,13 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = [AFMT_MP4_AAC_HE] = AFMT_ENTRY("AAC-HE","aac", NULL, get_mp4_metadata, "mp4\0"), /* AY (ZX Spectrum, Amstrad CPC Sound Format) */ - [AFMT_AY] = + [AFMT_AY] = AFMT_ENTRY("AY", "ay", NULL, get_ay_metadata, "ay\0"), + /* AY (ZX Spectrum Sound Format) */ +#ifdef HAVE_FPU + [AFMT_VTX] = + AFMT_ENTRY("VTX", "vtx", NULL, get_vtx_metadata, "vtx\0"), +#endif /* GBS (Game Boy Sound Format) */ [AFMT_GBS] = AFMT_ENTRY("GBS", "gbs", NULL, get_gbs_metadata, "gbs\0"), @@ -313,6 +318,9 @@ bool rbcodec_format_is_atomic(int afmt) case AFMT_MOD: case AFMT_SAP: case AFMT_AY: +#ifdef HAVE_FPU + case AFMT_VTX: +#endif case AFMT_GBS: case AFMT_HES: case AFMT_SGC: @@ -352,17 +360,17 @@ unsigned int probe_file_format(const char *filename) { char *suffix; unsigned int i; - + suffix = strrchr(filename, '.'); if (suffix == NULL) { return AFMT_UNKNOWN; } - + /* skip '.' */ suffix++; - + for (i = 1; i < AFMT_NUM_CODECS; i++) { /* search extension list for type */ @@ -379,7 +387,7 @@ unsigned int probe_file_format(const char *filename) } while (*ext != '\0'); } - + return AFMT_UNKNOWN; } @@ -418,7 +426,7 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname) close(logfd); } } - + /* Clear the mp3entry to avoid having bogus pointers appear */ wipe_mp3entry(id3); @@ -454,7 +462,7 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname) void strip_tags(int handle_id) { static const unsigned char tag[] = "TAG"; - static const unsigned char apetag[] = "APETAGEX"; + static const unsigned char apetag[] = "APETAGEX"; size_t len, version; void *tail; @@ -502,7 +510,7 @@ void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig) MOVE_ENTRY(entry->artist) MOVE_ENTRY(entry->album) - if (entry->genre_string > (char*)orig && + if (entry->genre_string > (char*)orig && entry->genre_string < (char*)orig + sizeof(struct mp3entry)) /* Don't adjust that if it points to an entry of the "genres" array */ entry->genre_string += offset; diff --git a/lib/rbcodec/metadata/metadata.h b/lib/rbcodec/metadata/metadata.h index 5c78eae9d4..fc9c1d062c 100644 --- a/lib/rbcodec/metadata/metadata.h +++ b/lib/rbcodec/metadata/metadata.h @@ -83,6 +83,9 @@ enum AFMT_MPC_SV8, /* Musepack SV8 */ AFMT_MP4_AAC_HE, /* Advanced Audio Coding (AAC-HE) in M4A container */ AFMT_AY, /* AY (ZX Spectrum, Amstrad CPC Sound Format) */ +#ifdef HAVE_FPU + AFMT_VTX, /* VTX (ZX Spectrum Sound Format) */ +#endif AFMT_GBS, /* GBS (Game Boy Sound Format) */ AFMT_HES, /* HES (Hudson Entertainment System Sound Format) */ AFMT_SGC, /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */ @@ -140,7 +143,7 @@ enum rec_format_indexes REC_FORMAT_CFG_NUM_BITS = 2 }; -#define REC_FORMAT_CFG_VAL_LIST "wave,aiff,wvpk,mpa3" +#define REC_FORMAT_CFG_VAL_LIST "wave,aiff,wvpk,mpa3" /* get REC_FORMAT_* corresponding AFMT_* */ extern const int rec_format_afmt[REC_NUM_FORMATS]; @@ -232,7 +235,7 @@ struct mp3entry { char* comment; char* albumartist; char* grouping; - int discnum; + int discnum; int tracknum; int layer; int year; @@ -283,7 +286,7 @@ struct mp3entry { #ifdef HAVE_TAGCACHE unsigned char autoresumable; /* caches result of autoresumable() */ - + /* runtime database fields */ long tagcache_idx; /* 0=invalid, otherwise idx+1 */ int rating; @@ -292,7 +295,7 @@ struct mp3entry { long lastplayed; long playtime; #endif - + /* replaygain support */ long track_level; /* holds the level in dB * (1<<FP_BITS) */ long album_level; @@ -329,5 +332,3 @@ bool rbcodec_format_is_atomic(int afmt); bool format_buffers_with_offset(int afmt); #endif - - diff --git a/lib/rbcodec/metadata/metadata_parsers.h b/lib/rbcodec/metadata/metadata_parsers.h index cc7d8a102e..6a4da51b63 100644 --- a/lib/rbcodec/metadata/metadata_parsers.h +++ b/lib/rbcodec/metadata/metadata_parsers.h @@ -49,6 +49,9 @@ bool get_vox_metadata(int fd, struct mp3entry* id3); bool get_wave64_metadata(int fd, struct mp3entry* id3); bool get_tta_metadata(int fd, struct mp3entry* id3); bool get_ay_metadata(int fd, struct mp3entry* id3); +#ifdef HAVE_FPU +bool get_vtx_metadata(int fd, struct mp3entry* id3); +#endif bool get_gbs_metadata(int fd, struct mp3entry* id3); bool get_hes_metadata(int fd, struct mp3entry* id3); bool get_sgc_metadata(int fd, struct mp3entry* id3); diff --git a/lib/rbcodec/metadata/vtx.c b/lib/rbcodec/metadata/vtx.c new file mode 100644 index 0000000000..eb06528b29 --- /dev/null +++ b/lib/rbcodec/metadata/vtx.c @@ -0,0 +1,150 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <inttypes.h> +#include "platform.h" + +#include "metadata.h" +#include "metadata_common.h" +#include "metadata_parsers.h" +#include "rbunicode.h" +#include "string-extra.h" + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef enum { + VTX_CHIP_AY = 0, /* emulate AY */ + VTX_CHIP_YM /* emulate YM */ +} vtx_chiptype_t; + +typedef enum { + VTX_LAYOUT_MONO = 0, + VTX_LAYOUT_ABC, + VTX_LAYOUT_ACB, + VTX_LAYOUT_BAC, + VTX_LAYOUT_BCA, + VTX_LAYOUT_CAB, + VTX_LAYOUT_CBA, + VTX_LAYOUT_CUSTOM +} vtx_layout_t; + +typedef struct { + vtx_chiptype_t chiptype; /* Type of sound chip */ + vtx_layout_t layout; /* stereo layout */ + uint loop; /* song loop */ + uint chipfreq; /* AY chip freq (1773400 for ZX) */ + uint playerfreq; /* 50 Hz for ZX, 60 Hz for yamaha */ + uint year; /* year song composed */ + char *title; /* song title */ + char *author; /* song author */ + char *from; /* song from */ + char *tracker; /* tracker */ + char *comment; /* comment */ + uint regdata_size; /* size of unpacked data */ + uint frames; /* number of AY data frames */ +} vtx_info_t; + +#define VTX_STRING_MAX 254 + +static uint Reader_ReadByte(int fd) { + unsigned char c; + read(fd, &c, sizeof(c)); + return c; +} + +static uint Reader_ReadWord(int fd) { + unsigned short s; + read(fd, &s, sizeof(s)); + return letoh16(s); +} + +static uint Reader_ReadDWord(int fd) { + unsigned int i; + read(fd, &i, sizeof(i)); + return letoh32(i); +} + +static char* Reader_ReadString(int fd, char *str) { + int i = 0; + char c = 1; + char *p = str; + + if (str) + *str = 0; + + while (i < VTX_STRING_MAX && c) { + read(fd, &c, sizeof(c)); + if (str) + *str++ = c; + i++; + } + + if (str) + *str = 0; + + return p; +} + +/* vtx info */ + +bool get_vtx_metadata(int fd, struct mp3entry* id3) +{ + vtx_info_t info; + char *p = id3->id3v2buf; + char buf[VTX_STRING_MAX+1]; + + if (lseek(fd, 0, SEEK_SET) < 0) + goto exit_bad; + + if (filesize(fd) < 20) + goto exit_bad; + + uint hdr = Reader_ReadWord(fd); + + if ((hdr != 0x7961) && (hdr != 0x6d79)) + goto exit_bad; + + info.layout = (vtx_layout_t)Reader_ReadByte(fd); + info.loop = Reader_ReadWord(fd); + info.chipfreq = Reader_ReadDWord(fd); + info.playerfreq = Reader_ReadByte(fd); + info.year = Reader_ReadWord(fd); + info.regdata_size = Reader_ReadDWord(fd); + info.frames = info.regdata_size / 14; + info.title = Reader_ReadString(fd, buf); + if (buf[0]) { + /* Title */ + id3->title = p; + p += strlcpy(p, info.title, VTX_STRING_MAX) + 1; + } + info.author = Reader_ReadString(fd, buf); + if (buf[0]) { + /* Artist */ + id3->artist = p; + p += strlcpy(p, info.author, VTX_STRING_MAX) + 1; + } + info.from = Reader_ReadString(fd, NULL); + info.tracker = Reader_ReadString(fd, NULL); + info.comment = Reader_ReadString(fd, buf); + if (buf[0]) { + /* Comment */ + id3->comment = p; + p += strlcpy(p, info.comment, VTX_STRING_MAX) + 1; + } + + id3->vbr = false; + id3->bitrate = 706; + id3->frequency = 44100; // XXX allow this to be configured? + + id3->filesize = filesize(fd); + id3->length = info.frames * 1000 / info.playerfreq; + + return true; + +exit_bad: + return false; +} |