summaryrefslogtreecommitdiff
path: root/apps/codecs
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2006-07-18 18:33:12 +0000
committerDave Chapman <dave@dchapman.com>2006-07-18 18:33:12 +0000
commit752faa4351f9caaa39d6c448490a1e740ea205b0 (patch)
tree7df40871ff8d387d869e47b2552356f3395a48e3 /apps/codecs
parentc5addb17eec7e0823c1880de737c018dc1881f37 (diff)
Patch #5157 by Rainer Sinsch - SID codec
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10237 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs')
-rw-r--r--apps/codecs/Makefile1
-rw-r--r--apps/codecs/SOURCES1
-rw-r--r--apps/codecs/sid.c1342
3 files changed, 1344 insertions, 0 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile
index f39eb55524..e365698625 100644
--- a/apps/codecs/Makefile
+++ b/apps/codecs/Makefile
@@ -47,6 +47,7 @@ all: $(ROCKS)
ifndef SIMVER
$(OBJDIR)/wav.elf : $(OBJDIR)/wav.o
+$(OBJDIR)/sid.elf : $(OBJDIR)/sid.o
$(OBJDIR)/aiff.elf : $(OBJDIR)/aiff.o
$(OBJDIR)/mpa.elf : $(OBJDIR)/mpa.o $(BUILDDIR)/libmad.a
$(OBJDIR)/a52.elf : $(OBJDIR)/a52.o $(BUILDDIR)/liba52.a
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES
index 05172d6da9..3537457313 100644
--- a/apps/codecs/SOURCES
+++ b/apps/codecs/SOURCES
@@ -12,4 +12,5 @@ aac.c
#endif
shorten.c
aiff.c
+sid.c
#endif
diff --git a/apps/codecs/sid.c b/apps/codecs/sid.c
new file mode 100644
index 0000000000..bdffb874f4
--- /dev/null
+++ b/apps/codecs/sid.c
@@ -0,0 +1,1342 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * SID Codec for rockbox based on the TinySID engine
+ *
+ * Written by Tammo Hinrichs (kb) and Rainer Sinsch in 1998-1999
+ * Ported to rockbox on 14 April 2006
+ *
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+ /*****************************
+ * kb explicitly points out that this emulation sounds crappy, though
+ * we decided to put it open source so everyone can enjoy sidmusic
+ * on rockbox
+ *
+ *****************************/
+
+ /*********************
+ * v1.1
+ * Added 16-04-2006: Rainer Sinsch
+ * Removed all time critical floating point operations and
+ * replaced them with quick & dirty integer calculations
+ *
+ * Added 17-04-2006: Rainer Sinsch
+ * Improved quick & dirty integer calculations for the resonant filter
+ * Improved audio quality by 4 bits
+ *
+ * v1.2
+ * Added 17-04-2006: Dave Chapman
+ * Improved file loading
+ *
+ * Added 17-04-2006: Rainer Sinsch
+ * Added sample routines
+ * Added cia timing routines
+ * Added fast forwarding capabilities
+ * Corrected bug in sid loading
+ *
+ * v1.2.1
+ * Added 04-05-2006: Rainer Sinsch
+ * Implemented Marco Alanens suggestion for subsong selection:
+ * Select the subsong by seeking: Each second represents a subsong
+ *
+ **************************/
+
+#define USE_FILTER
+
+#include "debug.h"
+#include "codeclib.h"
+#include <inttypes.h>
+
+CODEC_HEADER
+
+#define CHUNK_SIZE (1024*2)
+
+
+struct codec_api *rb;
+
+/* This codec supports SID Files:
+ *
+ */
+
+#ifdef USE_IRAM
+extern char iramcopy[];
+extern char iramstart[];
+extern char iramend[];
+extern char iedata[];
+extern char iend[];
+#endif
+
+static int32_t samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */
+
+/* Static buffer for the plain SID-File */
+static unsigned char sidfile[0x10000];
+
+void sidPoke(int reg, unsigned char val) ICODE_ATTR;
+
+#define FLAG_N 128
+#define FLAG_V 64
+#define FLAG_B 16
+#define FLAG_D 8
+#define FLAG_I 4
+#define FLAG_Z 2
+#define FLAG_C 1
+
+#define imp 0
+#define imm 1
+#define _abs 2
+#define absx 3
+#define absy 4
+#define zp 6
+#define zpx 7
+#define zpy 8
+#define ind 9
+#define indx 10
+#define indy 11
+#define acc 12
+#define rel 13
+
+enum {
+ adc, _and, asl, bcc, bcs, beq, bit, bmi, bne, bpl, brk, bvc, bvs, clc,
+ cld, cli, clv, cmp, cpx, cpy, dec, dex, dey, eor, inc, inx, iny, jmp,
+ jsr, lda, ldx, ldy, lsr, _nop, ora, pha, php, pla, plp, rol, ror, rti,
+ rts, sbc, sec, sed, sei, sta, stx, sty, tax, tay, tsx, txa, txs, tya,
+ xxx
+};
+
+/* SID register definition */
+struct s6581 {
+ struct sidvoice {
+ unsigned short freq;
+ unsigned short pulse;
+ unsigned char wave;
+ unsigned char ad;
+ unsigned char sr;
+ } v[3];
+ unsigned char ffreqlo;
+ unsigned char ffreqhi;
+ unsigned char res_ftv;
+ unsigned char ftp_vol;
+};
+
+/* internal oscillator def */
+struct sidosc {
+ unsigned long freq;
+ unsigned long pulse;
+ unsigned char wave;
+ unsigned char filter;
+ unsigned long attack;
+ unsigned long decay;
+ unsigned long sustain;
+ unsigned long release;
+ unsigned long counter;
+ signed long envval;
+ unsigned char envphase;
+ unsigned long noisepos;
+ unsigned long noiseval;
+ unsigned char noiseout;
+};
+
+/* internal filter def */
+struct sidflt {
+ int freq;
+ unsigned char l_ena;
+ unsigned char b_ena;
+ unsigned char h_ena;
+ unsigned char v3ena;
+ int vol;
+ int rez;
+ int h;
+ int b;
+ int l;
+};
+
+/* ------------------------ pseudo-constants (depending on mixing freq) */
+int mixing_frequency IDATA_ATTR;
+unsigned long freqmul IDATA_ATTR;
+int filtmul IDATA_ATTR;
+unsigned long attacks [16] IDATA_ATTR;
+unsigned long releases[16] IDATA_ATTR;
+
+/* ------------------------------------------------------------ globals */
+struct s6581 sid IDATA_ATTR;
+struct sidosc osc[3] IDATA_ATTR;
+struct sidflt filter IDATA_ATTR;
+
+/* ------------------------------------------------------ C64 Emu Stuff */
+unsigned char bval IDATA_ATTR;
+unsigned short wval IDATA_ATTR;
+/* -------------------------------------------------- Register & memory */
+unsigned char a,x,y,s,p IDATA_ATTR;
+unsigned short pc IDATA_ATTR;
+
+unsigned char memory[65536];
+
+/* ----------------------------------------- Variables for sample stuff */
+static int sample_active IDATA_ATTR;
+static int sample_position, sample_start, sample_end, sample_repeat_start IDATA_ATTR;
+static int fracPos IDATA_ATTR; /* Fractal position of sample */
+static int sample_period IDATA_ATTR;
+static int sample_repeats IDATA_ATTR;
+static int sample_order IDATA_ATTR;
+static int sample_nibble IDATA_ATTR;
+
+static int internal_period, internal_order, internal_start, internal_end,
+ internal_add, internal_repeat_times, internal_repeat_start IDATA_ATTR;
+
+/* ---------------------------------------------------------- constants */
+static const float attackTimes[16] ICONST_ATTR =
+{
+ 0.0022528606, 0.0080099577, 0.0157696042, 0.0237795619,
+ 0.0372963655, 0.0550684591, 0.0668330845, 0.0783473987,
+ 0.0981219818, 0.244554021, 0.489108042, 0.782472742,
+ 0.977715461, 2.93364701, 4.88907793, 7.82272493
+};
+static const float decayReleaseTimes[16] ICONST_ATTR =
+{
+ 0.00891777693, 0.024594051, 0.0484185907, 0.0730116639, 0.114512475,
+ 0.169078356, 0.205199432, 0.240551975, 0.301266125, 0.750858245,
+ 1.50171551, 2.40243682, 3.00189298, 9.00721405, 15.010998, 24.0182111
+};
+
+static const int opcodes[256] ICONST_ATTR = {
+ brk,ora,xxx,xxx,xxx,ora,asl,xxx,php,ora,asl,xxx,xxx,ora,asl,xxx,
+ bpl,ora,xxx,xxx,xxx,ora,asl,xxx,clc,ora,xxx,xxx,xxx,ora,asl,xxx,
+ jsr,_and,xxx,xxx,bit,_and,rol,xxx,plp,_and,rol,xxx,bit,_and,rol,xxx,
+ bmi,_and,xxx,xxx,xxx,_and,rol,xxx,sec,_and,xxx,xxx,xxx,_and,rol,xxx,
+ rti,eor,xxx,xxx,xxx,eor,lsr,xxx,pha,eor,lsr,xxx,jmp,eor,lsr,xxx,
+ bvc,eor,xxx,xxx,xxx,eor,lsr,xxx,cli,eor,xxx,xxx,xxx,eor,lsr,xxx,
+ rts,adc,xxx,xxx,xxx,adc,ror,xxx,pla,adc,ror,xxx,jmp,adc,ror,xxx,
+ bvs,adc,xxx,xxx,xxx,adc,ror,xxx,sei,adc,xxx,xxx,xxx,adc,ror,xxx,
+ xxx,sta,xxx,xxx,sty,sta,stx,xxx,dey,xxx,txa,xxx,sty,sta,stx,xxx,
+ bcc,sta,xxx,xxx,sty,sta,stx,xxx,tya,sta,txs,xxx,xxx,sta,xxx,xxx,
+ ldy,lda,ldx,xxx,ldy,lda,ldx,xxx,tay,lda,tax,xxx,ldy,lda,ldx,xxx,
+ bcs,lda,xxx,xxx,ldy,lda,ldx,xxx,clv,lda,tsx,xxx,ldy,lda,ldx,xxx,
+ cpy,cmp,xxx,xxx,cpy,cmp,dec,xxx,iny,cmp,dex,xxx,cpy,cmp,dec,xxx,
+ bne,cmp,xxx,xxx,xxx,cmp,dec,xxx,cld,cmp,xxx,xxx,xxx,cmp,dec,xxx,
+ cpx,sbc,xxx,xxx,cpx,sbc,inc,xxx,inx,sbc,_nop,xxx,cpx,sbc,inc,xxx,
+ beq,sbc,xxx,xxx,xxx,sbc,inc,xxx,sed,sbc,xxx,xxx,xxx,sbc,inc,xxx
+};
+
+
+static const int modes[256] ICONST_ATTR = {
+ imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
+ _abs,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
+ imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
+ imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,ind,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
+ imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,zpx,zpx,zpy,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx,
+ imm,indx,imm,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,zpx,zpx,zpy,xxx,imp,absy,acc,xxx,absx,absx,absy,xxx,
+ imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,zpx,zpx,zpx,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx,
+ imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx,
+ rel,indy,xxx,xxx,zpx,zpx,zpx,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx
+};
+
+/* Routines for quick & dirty float calculation */
+
+static inline int quickfloat_ConvertFromInt(int i)
+{
+ return (i<<16);
+}
+static inline int quickfloat_ConvertFromFloat(float f)
+{
+ return (int)(f*(1<<16));
+}
+static inline int quickfloat_Multiply(int a, int b)
+{
+ return (a>>8)*(b>>8);
+}
+static inline int quickfloat_ConvertToInt(int i)
+{
+ return (i>>16);
+}
+
+/* Get the bit from an unsigned long at a specified position */
+static inline unsigned char get_bit(unsigned long val, unsigned char b)
+{
+ return (unsigned char) ((val >> b) & 1);
+}
+
+
+static inline int GenerateDigi(int sIn)
+{
+ static int last_sample = 0;
+ static int sample = 0;
+
+ if (!sample_active) return(sIn);
+
+ if ((sample_position < sample_end) && (sample_position >= sample_start))
+ {
+ sIn += sample;
+
+ fracPos += 985248/sample_period;
+
+ if (fracPos > mixing_frequency)
+ {
+ fracPos%=mixing_frequency;
+
+ last_sample = sample;
+
+ // N�hstes Samples holen
+ if (sample_order == 0) {
+ sample_nibble++; // Nähstes Sample-Nibble
+ if (sample_nibble==2) {
+ sample_nibble = 0;
+ sample_position++;
+ }
+ }
+ else {
+ sample_nibble--;
+ if (sample_nibble < 0) {
+ sample_nibble=1;
+ sample_position++;
+ }
+ }
+ if (sample_repeats)
+ {
+ if (sample_position > sample_end)
+ {
+ sample_repeats--;
+ sample_position = sample_repeat_start;
+ }
+ else sample_active = 0;
+ }
+
+ sample = memory[sample_position&0xffff];
+ if (sample_nibble==1) // Hi-Nibble holen?
+ sample = (sample & 0xf0)>>4;
+ else sample = sample & 0x0f;
+
+ sample -= 7;
+ sample <<= 10;
+ }
+ }
+
+ return (sIn);
+}
+
+/* ------------------------------------------------------------- synthesis
+ initialize SID and frequency dependant values */
+void synth_init(unsigned long mixfrq) ICODE_ATTR;
+void synth_init(unsigned long mixfrq)
+{
+ int i;
+ mixing_frequency = mixfrq;
+ fracPos = 0;
+ freqmul = 15872000 / mixfrq;
+ filtmul = quickfloat_ConvertFromFloat(21.5332031f)/mixfrq;
+ for (i=0;i<16;i++) {
+ attacks [i]=(int) (0x1000000 / (attackTimes[i]*mixfrq));
+ releases[i]=(int) (0x1000000 / (decayReleaseTimes[i]*mixfrq));
+ }
+ memset(&sid,0,sizeof(sid));
+ memset(osc,0,sizeof(osc));
+ memset(&filter,0,sizeof(filter));
+ osc[0].noiseval = 0xffffff;
+ osc[1].noiseval = 0xffffff;
+ osc[2].noiseval = 0xffffff;
+}
+
+/* render a buffer of n samples with the actual register contents */
+void synth_render (int32_t *buffer, unsigned long len) ICODE_ATTR;
+void synth_render (int32_t *buffer, unsigned long len)
+{
+ unsigned long bp;
+ /* step 1: convert the not easily processable sid registers into some
+ more convenient and fast values (makes the thing much faster
+ if you process more than 1 sample value at once) */
+ unsigned char v;
+ for (v=0;v<3;v++) {
+ osc[v].pulse = (sid.v[v].pulse & 0xfff) << 16;
+ osc[v].filter = get_bit(sid.res_ftv,v);
+ osc[v].attack = attacks[sid.v[v].ad >> 4];
+ osc[v].decay = releases[sid.v[v].ad & 0xf];
+ osc[v].sustain = sid.v[v].sr & 0xf0;
+ osc[v].release = releases[sid.v[v].sr & 0xf];
+ osc[v].wave = sid.v[v].wave;
+ osc[v].freq = ((unsigned long)sid.v[v].freq)*freqmul;
+ }
+
+#ifdef USE_FILTER
+ filter.freq = (16*sid.ffreqhi + (sid.ffreqlo&0x7)) * filtmul;
+
+ if (filter.freq>quickfloat_ConvertFromInt(1))
+ filter.freq=quickfloat_ConvertFromInt(1);
+ /* the above line isnt correct at all - the problem is that the filter
+ works only up to rmxfreq/4 - this is sufficient for 44KHz but isnt
+ for 32KHz and lower - well, but sound quality is bad enough then to
+ neglect the fact that the filter doesnt come that high ;) */
+ filter.l_ena = get_bit(sid.ftp_vol,4);
+ filter.b_ena = get_bit(sid.ftp_vol,5);
+ filter.h_ena = get_bit(sid.ftp_vol,6);
+ filter.v3ena = !get_bit(sid.ftp_vol,7);
+ filter.vol = (sid.ftp_vol & 0xf);
+ filter.rez = quickfloat_ConvertFromFloat(1.2f) -
+ quickfloat_ConvertFromFloat(0.04f)*(sid.res_ftv >> 4);
+
+ /* We precalculate part of the quick float operation, saves time in loop later */
+ filter.rez>>=8;
+#endif
+
+
+ /* now render the buffer */
+ for (bp=0;bp<len;bp++) {
+#ifdef USE_FILTER
+ int outo=0;
+#endif
+ int outf=0;
+ /* step 2 : generate the two output signals (for filtered and non-
+ filtered) from the osc/eg sections */
+ for (v=0;v<3;v++) {
+ /* update wave counter */
+ osc[v].counter = (osc[v].counter+osc[v].freq) & 0xFFFFFFF;
+ /* reset counter / noise generator if reset get_bit set */
+ if (osc[v].wave & 0x08) {
+ osc[v].counter = 0;
+ osc[v].noisepos = 0;
+ osc[v].noiseval = 0xffffff;
+ }
+ unsigned char refosc = v?v-1:2; /* reference oscillator for sync/ring */
+ /* sync oscillator to refosc if sync bit set */
+ if (osc[v].wave & 0x02)
+ if (osc[refosc].counter < osc[refosc].freq)
+ osc[v].counter = osc[refosc].counter * osc[v].freq / osc[refosc].freq;
+ /* generate waveforms with really simple algorithms */
+ unsigned char triout = (unsigned char) (osc[v].counter>>19);
+ if (osc[v].counter>>27)
+ triout^=0xff;
+ unsigned char sawout = (unsigned char) (osc[v].counter >> 20);
+ unsigned char plsout = (unsigned char) ((osc[v].counter > osc[v].pulse)-1);
+
+ /* generate noise waveform exactly as the SID does. */
+ if (osc[v].noisepos!=(osc[v].counter>>23))
+ {
+ osc[v].noisepos = osc[v].counter >> 23;
+ osc[v].noiseval = (osc[v].noiseval << 1) |
+ (get_bit(osc[v].noiseval,22) ^ get_bit(osc[v].noiseval,17));
+ osc[v].noiseout = (get_bit(osc[v].noiseval,22) << 7) |
+ (get_bit(osc[v].noiseval,20) << 6) |
+ (get_bit(osc[v].noiseval,16) << 5) |
+ (get_bit(osc[v].noiseval,13) << 4) |
+ (get_bit(osc[v].noiseval,11) << 3) |
+ (get_bit(osc[v].noiseval, 7) << 2) |
+ (get_bit(osc[v].noiseval, 4) << 1) |
+ (get_bit(osc[v].noiseval, 2) << 0);
+ }
+ unsigned char nseout = osc[v].noiseout;
+
+ /* modulate triangle wave if ringmod bit set */
+ if (osc[v].wave & 0x04)
+ if (osc[refosc].counter < 0x8000000)
+ triout ^= 0xff;
+
+ /* now mix the oscillators with an AND operation as stated in
+ the SID's reference manual - even if this is completely wrong.
+ well, at least, the $30 and $70 waveform sounds correct and there's
+ no real solution to do $50 and $60, so who cares. */
+
+ unsigned char outv=0xFF;
+ if (osc[v].wave & 0x10) outv &= triout;
+ if (osc[v].wave & 0x20) outv &= sawout;
+ if (osc[v].wave & 0x40) outv &= plsout;
+ if (osc[v].wave & 0x80) outv &= nseout;
+
+ /* so now process the volume according to the phase and adsr values */
+ switch (osc[v].envphase) {
+ case 0 : { /* Phase 0 : Attack */
+ osc[v].envval+=osc[v].attack;
+ if (osc[v].envval >= 0xFFFFFF)
+ {
+ osc[v].envval = 0xFFFFFF;
+ osc[v].envphase = 1;
+ }
+ break;
+ }
+ case 1 : { /* Phase 1 : Decay */
+ osc[v].envval-=osc[v].decay;
+ if ((signed int) osc[v].envval <= (signed int) (osc[v].sustain<<16))
+ {
+ osc[v].envval = osc[v].sustain<<16;
+ osc[v].envphase = 2;
+ }
+ break;
+ }
+ case 2 : { /* Phase 2 : Sustain */
+ if ((signed int) osc[v].envval != (signed int) (osc[v].sustain<<16))
+ {
+ osc[v].envphase = 1;
+ }
+ /* :) yes, thats exactly how the SID works. and maybe
+ a music routine out there supports this, so better
+ let it in, thanks :) */
+ break;
+ }
+ case 3 : { /* Phase 3 : Release */
+ osc[v].envval-=osc[v].release;
+ if (osc[v].envval < 0x40000) osc[v].envval= 0x40000;
+
+ /* the volume offset is because the SID does not
+ completely silence the voices when it should. most
+ emulators do so though and thats the main reason
+ why the sound of emulators is too, err... emulated :) */
+ break;
+ }
+ }
+
+#ifdef USE_FILTER
+
+ /* now route the voice output to either the non-filtered or the
+ filtered channel and dont forget to blank out osc3 if desired */
+
+ if (v<2 || filter.v3ena)
+ {
+ if (osc[v].filter)
+ outf+=(((int)(outv-0x80))*osc[v].envval)>>22;
+ else
+ outo+=(((int)(outv-0x80))*osc[v].envval)>>22;
+ }
+#endif
+#ifndef USE_FILTER
+ /* Don't use filters, just mix all voices together */
+ outf+=((signed short)(outv-0x80)) * (osc[v].envval>>4);
+#endif
+ }
+
+
+#ifdef USE_FILTER
+ /* step 3
+ * so, now theres finally time to apply the multi-mode resonant filter
+ * to the signal. The easiest thing ist just modelling a real electronic
+ * filter circuit instead of fiddling around with complex IIRs or even
+ * FIRs ...
+ * it sounds as good as them or maybe better and needs only 3 MULs and
+ * 4 ADDs for EVERYTHING. SIDPlay uses this kind of filter, too, but
+ * Mage messed the whole thing completely up - as the rest of the
+ * emulator.
+ * This filter sounds a lot like the 8580, as the low-quality, dirty
+ * sound of the 6581 is uuh too hard to achieve :) */
+
+ filter.h = quickfloat_ConvertFromInt(outf) - (filter.b>>8)*filter.rez - filter.l;
+ filter.b += quickfloat_Multiply(filter.freq, filter.h);
+ filter.l += quickfloat_Multiply(filter.freq, filter.b);
+
+ outf = 0;
+
+ if (filter.l_ena) outf+=quickfloat_ConvertToInt(filter.l);
+ if (filter.b_ena) outf+=quickfloat_ConvertToInt(filter.b);
+ if (filter.h_ena) outf+=quickfloat_ConvertToInt(filter.h);
+
+ int final_sample = (filter.vol*(outo+outf));
+ *(buffer+bp)= GenerateDigi(final_sample)<<13;
+#endif
+#ifndef USE_FILTER
+ *(buffer+bp) = GenerateDigi(outf)<<3;
+#endif
+ }
+}
+
+
+
+/*
+* C64 Mem Routines
+*/
+static inline unsigned char getmem(unsigned short addr)
+{
+ return memory[addr];
+}
+
+static inline void setmem(unsigned short addr, unsigned char value)
+{
+ if ((addr&0xfc00)==0xd400)
+ {
+ sidPoke(addr&0x1f,value);
+ /* New SID-Register */
+ if (addr > 0xd418)
+ {
+ switch (addr)
+ {
+ case 0xd41f: /* Start-Hi */
+ internal_start = (internal_start&0x00ff) | (value<<8); break;
+ case 0xd41e: /* Start-Lo */
+ internal_start = (internal_start&0xff00) | (value); break;
+ case 0xd47f: /* Repeat-Hi */
+ internal_repeat_start = (internal_repeat_start&0x00ff) | (value<<8); break;
+ case 0xd47e: /* Repeat-Lo */
+ internal_repeat_start = (internal_repeat_start&0xff00) | (value); break;
+ case 0xd43e: /* End-Hi */
+ internal_end = (internal_end&0x00ff) | (value<<8); break;
+ case 0xd43d: /* End-Lo */
+ internal_end = (internal_end&0xff00) | (value); break;
+ case 0xd43f: /* Loop-Size */
+ internal_repeat_times = value; break;
+ case 0xd45e: /* Period-Hi */
+ internal_period = (internal_period&0x00ff) | (value<<8); break;
+ case 0xd45d: /* Period-Lo */
+ internal_period = (internal_period&0xff00) | (value); break;
+ case 0xd47d: /* Sample Order */
+ internal_order = value; break;
+ case 0xd45f: /* Sample Add */
+ internal_add = value; break;
+ case 0xd41d: /* Start sampling */
+ sample_repeats = internal_repeat_times;
+ sample_position = internal_start;
+ sample_start = internal_start;
+ sample_end = internal_end;
+ sample_repeat_start = internal_repeat_start;
+ sample_period = internal_period;
+ sample_order = internal_order;
+ switch (value)
+ {
+ case 0xfd: sample_active = 0; break;
+ case 0xfe:
+ case 0xff: sample_active = 1; break;
+ default: return;
+ }
+ break;
+ }
+ }
+ }
+ else memory[addr]=value;
+}
+
+/*
+* Poke a value into the sid register
+*/
+void sidPoke(int reg, unsigned char val) ICODE_ATTR;
+void sidPoke(int reg, unsigned char val)
+{
+ int voice=0;
+
+ if ((reg >= 7) && (reg <=13)) {voice=1; reg-=7;}
+ else if ((reg >= 14) && (reg <=20)) {voice=2; reg-=14;}
+
+ switch (reg) {
+ case 0: { /* Set frequency: Low byte */
+ sid.v[voice].freq = (sid.v[voice].freq&0xff00)+val;
+ break;
+ }
+ case 1: { /* Set frequency: High byte */
+ sid.v[voice].freq = (sid.v[voice].freq&0xff)+(val<<8);
+ break;
+ }
+ case 2: { /* Set pulse width: Low byte */
+ sid.v[voice].pulse = (sid.v[voice].pulse&0xff00)+val;
+ break;
+ }
+ case 3: { /* Set pulse width: High byte */
+ sid.v[voice].pulse = (sid.v[voice].pulse&0xff)+(val<<8);
+ break;
+ }
+ case 4: { sid.v[voice].wave = val;
+ /* Directly look at GATE-Bit!
+ * a change may happen twice or more often during one cpujsr
+ * Put the Envelope Generator into attack or release phase if desired
+ */
+ if ((val & 0x01) == 0) osc[voice].envphase=3;
+ else if (osc[voice].envphase==3) osc[voice].envphase=0;
+ break;
+ }
+
+ case 5: { sid.v[voice].ad = val; break;}
+ case 6: { sid.v[voice].sr = val; break;}
+
+ case 21: { sid.ffreqlo = val; break; }
+ case 22: { sid.ffreqhi = val; break; }
+ case 23: { sid.res_ftv = val; break; }
+ case 24: { sid.ftp_vol = val; break;}
+ }
+ return;
+}
+
+static inline unsigned char getaddr(int mode)
+{
+ unsigned short ad,ad2;
+ switch(mode)
+ {
+ case imp:
+ return 0;
+ case imm:
+ return getmem(pc++);
+ case _abs:
+ ad=getmem(pc++);
+ ad|=256*getmem(pc++);
+ return getmem(ad);
+ case absx:
+ ad=getmem(pc++);
+ ad|=256*getmem(pc++);
+ ad2=ad+x;
+ return getmem(ad2);
+ case absy:
+ ad=getmem(pc++);
+ ad|=256*getmem(pc++);
+ ad2=ad+y;
+ return getmem(ad2);
+ case zp:
+ ad=getmem(pc++);
+ return getmem(ad);
+ case zpx:
+ ad=getmem(pc++);
+ ad+=x;
+ return getmem(ad&0xff);
+ case zpy:
+ ad=getmem(pc++);
+ ad+=y;
+ return getmem(ad&0xff);
+ case indx:
+ ad=getmem(pc++);
+ ad+=x;
+ ad2=getmem(ad&0xff);
+ ad++;
+ ad2|=getmem(ad&0xff)<<8;
+ return getmem(ad2);
+ case indy:
+ ad=getmem(pc++);
+ ad2=getmem(ad);
+ ad2|=getmem((ad+1)&0xff)<<8;
+ ad=ad2+y;
+ return getmem(ad);
+ case acc:
+ return a;
+ }
+ return 0;
+}
+
+static inline void setaddr(int mode, unsigned char val)
+{
+ unsigned short ad,ad2;
+ switch(mode)
+ {
+ case _abs:
+ ad=getmem(pc-2);
+ ad|=256*getmem(pc-1);
+ setmem(ad,val);
+ return;
+ case absx:
+ ad=getmem(pc-2);
+ ad|=256*getmem(pc-1);
+ ad2=ad+x;
+ setmem(ad2,val);
+ return;
+ case zp:
+ ad=getmem(pc-1);
+ setmem(ad,val);
+ return;
+ case zpx:
+ ad=getmem(pc-1);
+ ad+=x;
+ setmem(ad&0xff,val);
+ return;
+ case acc:
+ a=val;
+ return;
+ }
+}
+
+
+static inline void putaddr(int mode, unsigned char val)
+{
+ unsigned short ad,ad2;
+ switch(mode)
+ {
+ case _abs:
+ ad=getmem(pc++);
+ ad|=getmem(pc++)<<8;
+ setmem(ad,val);
+ return;
+ case absx:
+ ad=getmem(pc++);
+ ad|=getmem(pc++)<<8;
+ ad2=ad+x;
+ setmem(ad2,val);
+ return;
+ case absy:
+ ad=getmem(pc++);
+ ad|=getmem(pc++)<<8;
+ ad2=ad+y;
+ setmem(ad2,val);
+ return;
+ case zp:
+ ad=getmem(pc++);
+ setmem(ad,val);
+ return;
+ case zpx:
+ ad=getmem(pc++);
+ ad+=x;
+ setmem(ad&0xff,val);
+ return;
+ case zpy:
+ ad=getmem(pc++);
+ ad+=y;
+ setmem(ad&0xff,val);
+ return;
+ case indx:
+ ad=getmem(pc++);
+ ad+=x;
+ ad2=getmem(ad&0xff);
+ ad++;
+ ad2|=getmem(ad&0xff)<<8;
+ setmem(ad2,val);
+ return;
+ case indy:
+ ad=getmem(pc++);
+ ad2=getmem(ad);
+ ad2|=getmem((ad+1)&0xff)<<8;
+ ad=ad2+y;
+ setmem(ad,val);
+ return;
+ case acc:
+ a=val;
+ return;
+ }
+}
+
+
+static inline void setflags(int flag, int cond)
+{
+ if (cond) p|=flag;
+ else p&=~flag;
+}
+
+
+static inline void push(unsigned char val)
+{
+ setmem(0x100+s,val);
+ if (s) s--;
+}
+
+static inline unsigned char pop(void)
+{
+ if (s<0xff) s++;
+ return getmem(0x100+s);
+}
+
+static inline void branch(int flag)
+{
+ signed char dist;
+ dist=(signed char)getaddr(imm);
+ wval=pc+dist;
+ if (flag) pc=wval;
+}
+
+void cpuReset(void) ICODE_ATTR;
+void cpuReset(void)
+{
+ a=x=y=0;
+ p=0;
+ s=255;
+ pc=getaddr(0xfffc);
+}
+
+void cpuResetTo(unsigned short npc, unsigned char na) ICODE_ATTR;
+void cpuResetTo(unsigned short npc, unsigned char na)
+{
+ a=na;
+ x=0;
+ y=0;
+ p=0;
+ s=255;
+ pc=npc;
+}
+
+static inline void cpuParse(void)
+{
+ unsigned char opc=getmem(pc++);
+ int cmd=opcodes[opc];
+ int addr=modes[opc];
+ int c;
+ switch (cmd)
+ {
+ case adc:
+ wval=(unsigned short)a+getaddr(addr)+((p&FLAG_C)?1:0);
+ setflags(FLAG_C, wval&0x100);
+ a=(unsigned char)wval;
+ setflags(FLAG_Z, !a);
+ setflags(FLAG_N, a&0x80);
+ setflags(FLAG_V, (!!(p&FLAG_C)) ^ (!!(p&FLAG_N)));
+ break;
+ case _and:
+ bval=getaddr(addr);
+ a&=bval;
+ setflags(FLAG_Z, !a);
+ setflags(FLAG_N, a&0x80);
+ break;
+ case asl:
+ wval=getaddr(addr);
+ wval<<=1;
+ setaddr(addr,(unsigned char)wval);
+ setflags(FLAG_Z,!wval);
+ setflags(FLAG_N,wval&0x80);
+ setflags(FLAG_C,wval&0x100);
+ break;
+ case bcc:
+ branch(!(p&FLAG_C));
+ break;
+ case bcs:
+ branch(p&FLAG_C);
+ break;
+ case bne:
+ branch(!(p&FLAG_Z));
+ break;
+ case beq:
+ branch(p&FLAG_Z);
+ break;
+ case bpl:
+ branch(!(p&FLAG_N));
+ break;
+ case bmi:
+ branch(p&FLAG_N);
+ break;
+ case bvc:
+ branch(!(p&FLAG_V));
+ break;
+ case bvs:
+ branch(p&FLAG_V);
+ break;
+ case bit:
+ bval=getaddr(addr);
+ setflags(FLAG_Z,!(a&bval));
+ setflags(FLAG_N,bval&0x80);
+ setflags(FLAG_V,bval&0x40);
+ break;
+ case brk:
+ pc=0; /* Just quit the emulation */
+ break;
+ case clc:
+ setflags(FLAG_C,0);
+ break;
+ case cld:
+ setflags(FLAG_D,0);
+ break;
+ case cli:
+ setflags(FLAG_I,0);
+ break;
+ case clv:
+ setflags(FLAG_V,0);
+ break;
+ case cmp:
+ bval=getaddr(addr);
+ wval=(unsigned short)a-bval;
+ setflags(FLAG_Z,!wval);
+ setflags(FLAG_N,wval&0x80);
+ setflags(FLAG_C,a>=bval);
+ break;
+ case cpx:
+ bval=getaddr(addr);
+ wval=(unsigned short)x-bval;
+ setflags(FLAG_Z,!wval);
+ setflags(FLAG_N,wval&0x80);
+ setflags(FLAG_C,x>=bval);
+ break;
+ case cpy:
+ bval=getaddr(addr);
+ wval=(unsigned short)y-bval;
+ setflags(FLAG_Z,!wval);
+ setflags(FLAG_N,wval&0x80);
+ setflags(FLAG_C,y>=bval);
+ break;
+ case dec:
+ bval=getaddr(addr);
+ bval--;
+ setaddr(addr,bval);
+ setflags(FLAG_Z,!bval);
+ setflags(FLAG_N,bval&0x80);
+ break;
+ case dex:
+ x--;
+ setflags(FLAG_Z,!x);
+ setflags(FLAG_N,x&0x80);
+ break;
+ case dey:
+ y--;
+ setflags(FLAG_Z,!y);
+ setflags(FLAG_N,y&0x80);
+ break;
+ case eor:
+ bval=getaddr(addr);
+ a^=bval;
+ setflags(FLAG_Z,!a);
+ setflags(FLAG_N,a&0x80);
+ break;
+ case inc:
+ bval=getaddr(addr);
+ bval++;
+ setaddr(addr,bval);
+ setflags(FLAG_Z,!bval);
+ setflags(FLAG_N,bval&0x80);
+ break;
+ case inx:
+ x++;
+ setflags(FLAG_Z,!x);
+ setflags(FLAG_N,x&0x80);
+ break;
+ case iny:
+ y++;
+ setflags(FLAG_Z,!y);
+ setflags(FLAG_N,y&0x80);
+ break;
+ case jmp:
+ wval=getmem(pc++);
+ wval|=256*getmem(pc++);
+ switch (addr)
+ {
+ case _abs:
+ pc=wval;
+ break;
+ case ind:
+ pc=getmem(wval);
+ pc|=256*getmem(wval+1);
+ break;
+ }
+ break;
+ case jsr:
+ push((pc+1)>>8);
+ push((pc+1));
+ wval=getmem(pc++);
+ wval|=256*getmem(pc++);
+ pc=wval;
+ break;
+ case lda:
+ a=getaddr(addr);
+ setflags(FLAG_Z,!a);
+ setflags(FLAG_N,a&0x80);
+ break;
+ case ldx:
+ x=getaddr(addr);
+ setflags(FLAG_Z,!x);
+ setflags(FLAG_N,x&0x80);
+ break;
+ case ldy:
+ y=getaddr(addr);
+ setflags(FLAG_Z,!y);
+ setflags(FLAG_N,y&0x80);
+ break;
+ case lsr:
+ bval=getaddr(addr); wval=(unsigned char)bval;
+ wval>>=1;
+ setaddr(addr,(unsigned char)wval);
+ setflags(FLAG_Z,!wval);
+ setflags(FLAG_N,wval&0x80);
+ setflags(FLAG_C,bval&1);
+ break;
+ case _nop:
+ break;
+ case ora:
+ bval=getaddr(addr);
+ a|=bval;
+ setflags(FLAG_Z,!a);
+ setflags(FLAG_N,a&0x80);
+ break;
+ case pha:
+ push(a);
+ break;
+ case php:
+ push(p);
+ break;
+ case pla:
+ a=pop();
+ setflags(FLAG_Z,!a);
+ setflags(FLAG_N,a&0x80);
+ break;
+ case plp:
+ p=pop();
+ break;
+ case rol:
+ bval=getaddr(addr);
+ c=!!(p&FLAG_C);
+ setflags(FLAG_C,bval&0x80);
+ bval<<=1;
+ bval|=c;
+ setaddr(addr,bval);
+ setflags(FLAG_N,bval&0x80);
+ setflags(FLAG_Z,!bval);
+ break;
+ case ror:
+ bval=getaddr(addr);
+ c=!!(p&FLAG_C);
+ setflags(FLAG_C,bval&1);
+ bval>>=1;
+ bval|=128*c;
+ setaddr(addr,bval);
+ setflags(FLAG_N,bval&0x80);
+ setflags(FLAG_Z,!bval);
+ break;
+ case rti:
+ /* Treat RTI like RTS */
+ case rts:
+ wval=pop();
+ wval|=pop()<<8;
+ pc=wval+1;
+ break;
+ case sbc:
+ bval=getaddr(addr)^0xff;
+ wval=(unsigned short)a+bval+((p&FLAG_C)?1:0);
+ setflags(FLAG_C, wval&0x100);
+ a=(unsigned char)wval;
+ setflags(FLAG_Z, !a);
+ setflags(FLAG_N, a>127);
+ setflags(FLAG_V, (!!(p&FLAG_C)) ^ (!!(p&FLAG_N)));
+ break;
+ case sec:
+ setflags(FLAG_C,1);
+ break;
+ case sed:
+ setflags(FLAG_D,1);
+ break;
+ case sei:
+ setflags(FLAG_I,1);
+ break;
+ case sta:
+ putaddr(addr,a);
+ break;
+ case stx:
+ putaddr(addr,x);
+ break;
+ case sty:
+ putaddr(addr,y);
+ break;
+ case tax:
+ x=a;
+ setflags(FLAG_Z, !x);
+ setflags(FLAG_N, x&0x80);
+ break;
+ case tay:
+ y=a;
+ setflags(FLAG_Z, !y);
+ setflags(FLAG_N, y&0x80);
+ break;
+ case tsx:
+ x=s;
+ setflags(FLAG_Z, !x);
+ setflags(FLAG_N, x&0x80);
+ break;
+ case txa:
+ a=x;
+ setflags(FLAG_Z, !a);
+ setflags(FLAG_N, a&0x80);
+ break;
+ case txs:
+ s=x;
+ break;
+ case tya:
+ a=y;
+ setflags(FLAG_Z, !a);
+ setflags(FLAG_N, a&0x80);
+ break;
+ }
+}
+
+void cpuJSR(unsigned short npc, unsigned char na) ICODE_ATTR;
+void cpuJSR(unsigned short npc, unsigned char na)
+{
+ a=na;
+ x=0;
+ y=0;
+ p=0;
+ s=255;
+ pc=npc;
+ push(0);
+ push(0);
+
+ while (pc > 1)
+ cpuParse();
+
+}
+
+void c64Init(int nSampleRate) ICODE_ATTR;
+void c64Init(int nSampleRate)
+{
+ synth_init(nSampleRate);
+ memset(memory, 0, sizeof(memory));
+
+ cpuReset();
+}
+
+
+
+unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr,
+ unsigned short *init_addr, unsigned short *play_addr, unsigned char *subsongs, unsigned char *startsong, unsigned char *speed, unsigned short size) ICODE_ATTR;
+unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr,
+ unsigned short *init_addr, unsigned short *play_addr, unsigned char *subsongs, unsigned char *startsong, unsigned char *speed, unsigned short size)
+{
+ unsigned char *pData;
+ unsigned char data_file_offset;
+
+ pData = (unsigned char*)pSidData;
+ data_file_offset = pData[7];
+
+ *load_addr = pData[8]<<8;
+ *load_addr|= pData[9];
+
+ *init_addr = pData[10]<<8;
+ *init_addr|= pData[11];
+
+ *play_addr = pData[12]<<8;
+ *play_addr|= pData[13];
+
+ *subsongs = pData[0xf]-1;
+ *startsong = pData[0x11]-1;
+
+ *load_addr = pData[data_file_offset];
+ *load_addr|= pData[data_file_offset+1]<<8;
+
+ *speed = pData[0x15];
+
+ memset(memory, 0, sizeof(memory));
+ memcpy(&memory[*load_addr], &pData[data_file_offset+2], size-(data_file_offset+2));
+
+ if (*play_addr == 0)
+ {
+ cpuJSR(*init_addr, 0);
+ *play_addr = (memory[0x0315]<<8)+memory[0x0314];
+ }
+
+ return *load_addr;
+}
+
+
+enum codec_status codec_start(struct codec_api *api)
+{
+ struct codec_api *ci;
+ size_t n, bytesfree;
+ unsigned char *p;
+ unsigned int filesize;
+
+ unsigned short load_addr, init_addr, play_addr;
+ unsigned char subSongsMax, subSong, song_speed;
+
+ int nSamplesRendered = 0;
+ int nSamplesPerCall = 882; /* This is PAL SID single speed (44100/50Hz) */
+ int nSamplesToRender = 0;
+
+ /* Generic codec initialisation */
+ rb = api;
+ ci = api;
+
+
+#ifdef USE_IRAM
+ ci->memcpy(iramstart, iramcopy, iramend - iramstart);
+ ci->memset(iedata, 0, iend - iedata);
+#endif
+
+ ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
+ ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));
+ ci->configure(DSP_DITHER, (bool *)false);
+
+next_track:
+ if (codec_init(api)) {
+ return CODEC_ERROR;
+ }
+
+ while (!*ci->taginfo_ready)
+ ci->yield();
+
+ /* Load SID file */
+ p = sidfile;
+ bytesfree=sizeof(sidfile);
+ while ((n = rb->read_filebuf(p, bytesfree)) > 0) {
+ p += n;
+ bytesfree -= n;
+ }
+ filesize = p-sidfile;
+
+ if (filesize == 0)
+ return CODEC_ERROR;
+
+ c64Init(44100);
+ LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, &subSongsMax, &subSong, &song_speed, filesize);
+ sidPoke(24, 15); /* Turn on full volume */
+ cpuJSR(init_addr, subSong); /* Start the song initialize */
+
+
+ /* Make use of 44.1khz */
+ ci->configure(DSP_SET_FREQUENCY, (long *)44100);
+ /* Sample depth is 28 bit host endian */
+ ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)28);
+ /* Mono output */
+ ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
+
+
+ /* Set the elapsed time to the current subsong (in seconds) */
+ ci->set_elapsed(subSong*1000);
+
+ /* The main decoder loop */
+ while (1) {
+ ci->yield();
+ if (ci->stop_codec || ci->new_track)
+ break;
+
+ if (ci->seek_time) {
+ /* New time is ready in ci->seek_time */
+
+ /* Start playing from scratch */
+ c64Init(44100);
+ LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, &subSongsMax, &subSong, &song_speed, filesize);
+ sidPoke(24, 15); /* Turn on full volume */
+ subSong = ci->seek_time / 1000; /* Now use the current seek time in seconds as subsong */
+ cpuJSR(init_addr, subSong); /* Start the song initialize */
+ nSamplesToRender = 0; /* Start the rendering from scratch */
+
+ ci->seek_complete();
+
+ /* Set the elapsed time to the current subsong (in seconds) */
+ ci->set_elapsed(subSong*1000);
+ }
+
+ nSamplesRendered = 0;
+ while (nSamplesRendered < CHUNK_SIZE)
+ {
+ if (nSamplesToRender == 0)
+ {
+ cpuJSR(play_addr, 0);
+
+ /* Find out if cia timing is used and how many samples
+ have to be calculated for each cpujsr */
+ int nRefreshCIA = (int)(20000*(memory[0xdc04]|(memory[0xdc05]<<8))/0x4c00);
+ if ((nRefreshCIA==0) || (song_speed == 0))
+ nRefreshCIA = 20000;
+ nSamplesPerCall = mixing_frequency*nRefreshCIA/1000000;
+
+ nSamplesToRender = nSamplesPerCall;
+ }
+ if (nSamplesRendered + nSamplesToRender > CHUNK_SIZE)
+ {
+ synth_render(samples+nSamplesRendered, CHUNK_SIZE-nSamplesRendered);
+ nSamplesToRender -= CHUNK_SIZE-nSamplesRendered;
+ nSamplesRendered = CHUNK_SIZE;
+ }
+ else
+ {
+ synth_render(samples+nSamplesRendered, nSamplesToRender);
+ nSamplesRendered += nSamplesToRender;
+ nSamplesToRender = 0;
+ }
+ }
+
+ while (!ci->pcmbuf_insert((char *)samples, CHUNK_SIZE*4))
+ ci->yield();
+ }
+
+ if (ci->request_next_track())
+ goto next_track;
+
+ return CODEC_OK;
+}