FS#11052 -- SID Playback in Stereo

Original patch by Stefan Waigand
Updated by Igor Poretsky

Change-Id: Icaf7beb8349ab90e21b94baee627c9412cb2b55d
This commit is contained in:
Solomon Peachy 2019-07-24 13:56:58 -04:00
parent a89bf68e88
commit f2fd8fe79b
2 changed files with 121 additions and 109 deletions

View file

@ -680,6 +680,7 @@ Juan Gonzalez
Yangyong Wu Yangyong Wu
Andy Potter Andy Potter
Moshe Piekarski Moshe Piekarski
Stefan Waigand
The libmad team The libmad team
The wavpack team The wavpack team

View file

@ -8,7 +8,7 @@
* $Id$ * $Id$
* *
* SID Codec for rockbox based on the TinySID engine * SID Codec for rockbox based on the TinySID engine
* *
* Written by Tammo Hinrichs (kb) and Rainer Sinsch in 1998-1999 * Written by Tammo Hinrichs (kb) and Rainer Sinsch in 1998-1999
* Ported to rockbox on 14 April 2006 * Ported to rockbox on 14 April 2006
* *
@ -29,7 +29,7 @@
* on rockbox * on rockbox
* *
*****************************/ *****************************/
/********************* /*********************
* v1.1 * v1.1
* Added 16-04-2006: Rainer Sinsch * Added 16-04-2006: Rainer Sinsch
@ -39,22 +39,26 @@
* Added 17-04-2006: Rainer Sinsch * Added 17-04-2006: Rainer Sinsch
* Improved quick & dirty integer calculations for the resonant filter * Improved quick & dirty integer calculations for the resonant filter
* Improved audio quality by 4 bits * Improved audio quality by 4 bits
* *
* v1.2 * v1.2
* Added 17-04-2006: Dave Chapman * Added 17-04-2006: Dave Chapman
* Improved file loading * Improved file loading
* *
* Added 17-04-2006: Rainer Sinsch * Added 17-04-2006: Rainer Sinsch
* Added sample routines * Added sample routines
* Added cia timing routines * Added cia timing routines
* Added fast forwarding capabilities * Added fast forwarding capabilities
* Corrected bug in sid loading * Corrected bug in sid loading
* *
* v1.2.1 * v1.2.1
* Added 04-05-2006: Rainer Sinsch * Added 04-05-2006: Rainer Sinsch
* Implemented Marco Alanens suggestion for subsong selection: * Implemented Marco Alanens suggestion for subsong selection:
* Select the subsong by seeking: Each second represents a subsong * Select the subsong by seeking: Each second represents a subsong
* *
* v1.3
* Added 25-07-2019: Stefan Waigand, Igor Poretsky, Solomon Peachy
* SID playback in stereo (See FS#11052)
*
**************************/ **************************/
#define USE_FILTER #define USE_FILTER
@ -70,10 +74,12 @@ CODEC_HEADER
#define SAMPLE_RATE 44100 #define SAMPLE_RATE 44100
/* This codec supports SID Files: /* This codec supports SID Files:
* *
*/ */
static int32_t samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */ /* The sample buffers */
static int32_t samples_r[CHUNK_SIZE] IBSS_ATTR;
static int32_t samples_l[CHUNK_SIZE] IBSS_ATTR;
void sidPoke(int reg, unsigned char val) ICODE_ATTR; void sidPoke(int reg, unsigned char val) ICODE_ATTR;
@ -99,7 +105,7 @@ void sidPoke(int reg, unsigned char val) ICODE_ATTR;
#define acc 12 #define acc 12
#define rel 13 #define rel 13
enum { enum {
adc, _and, asl, bcc, bcs, beq, bit, bmi, bne, bpl, _brk, bvc, bvs, clc, 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, 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, jsr, lda, ldx, ldy, lsr, _nop, ora, pha, php, pla, plp, rol, ror, rti,
@ -215,9 +221,9 @@ static const unsigned long attacks[16] ICONST_ATTR =
}; };
static const unsigned long releases[16] ICONST_ATTR = static const unsigned long releases[16] ICONST_ATTR =
{ {
DIV(0.00891777693), DIV(0.0245940510), DIV(0.0484185907), DIV(0.0730116639), DIV(0.00891777693), DIV(0.0245940510), DIV(0.0484185907), DIV(0.0730116639),
DIV(0.11451247500), DIV(0.1690783560), DIV(0.2051994320), DIV(0.2405519750), DIV(0.11451247500), DIV(0.1690783560), DIV(0.2051994320), DIV(0.2405519750),
DIV(0.30126612500), DIV(0.7508582450), DIV(1.5017155100), DIV(2.4024368200), DIV(0.30126612500), DIV(0.7508582450), DIV(1.5017155100), DIV(2.4024368200),
DIV(3.00189298000), DIV(9.0072140500), DIV(15.010998000), DIV(24.018211100) DIV(3.00189298000), DIV(9.0072140500), DIV(15.010998000), DIV(24.018211100)
}; };
#endif #endif
@ -299,13 +305,13 @@ static inline int GenerateDigi(int sIn)
if ((sample_position < sample_end) && (sample_position >= sample_start)) if ((sample_position < sample_end) && (sample_position >= sample_start))
{ {
sIn += sample; sIn += sample;
fracPos += 985248/sample_period; fracPos += 985248/sample_period;
if (fracPos > mixing_frequency) if (fracPos > mixing_frequency)
{ {
fracPos%=mixing_frequency; fracPos%=mixing_frequency;
// N<>hstes Samples holen // N<>hstes Samples holen
if (sample_order == 0) { if (sample_order == 0) {
sample_nibble++; // Nähstes Sample-Nibble sample_nibble++; // Nähstes Sample-Nibble
@ -320,24 +326,24 @@ static inline int GenerateDigi(int sIn)
sample_nibble=1; sample_nibble=1;
sample_position++; sample_position++;
} }
} }
if (sample_repeats) if (sample_repeats)
{ {
if (sample_position > sample_end) if (sample_position > sample_end)
{ {
sample_repeats--; sample_repeats--;
sample_position = sample_repeat_start; sample_position = sample_repeat_start;
} }
else sample_active = 0; else sample_active = 0;
} }
sample = memory[sample_position&0xffff]; sample = memory[sample_position&0xffff];
if (sample_nibble==1) // Hi-Nibble holen? if (sample_nibble==1) // Hi-Nibble holen?
sample = (sample & 0xf0)>>4; sample = (sample & 0xf0)>>4;
else sample = sample & 0x0f; else sample = sample & 0x0f;
sample -= 7; sample -= 7;
sample <<= 10; sample <<= 10;
} }
} }
@ -367,12 +373,12 @@ void synth_init(unsigned long mixfrq)
memset(&filter,0,sizeof(filter)); memset(&filter,0,sizeof(filter));
osc[0].noiseval = 0xffffff; osc[0].noiseval = 0xffffff;
osc[1].noiseval = 0xffffff; osc[1].noiseval = 0xffffff;
osc[2].noiseval = 0xffffff; osc[2].noiseval = 0xffffff;
} }
/* render a buffer of n samples with the actual register contents */ /* 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_r, int32_t *buffer_l, unsigned long len) ICODE_ATTR;
void synth_render (int32_t *buffer, unsigned long len) void synth_render (int32_t *buffer_r, int32_t *buffer_l, unsigned long len)
{ {
unsigned long bp; unsigned long bp;
/* step 1: convert the not easily processable sid registers into some /* step 1: convert the not easily processable sid registers into some
@ -392,7 +398,7 @@ void synth_render (int32_t *buffer, unsigned long len)
#ifdef USE_FILTER #ifdef USE_FILTER
filter.freq = (16*sid.ffreqhi + (sid.ffreqlo&0x7)) * filtmul; filter.freq = (16*sid.ffreqhi + (sid.ffreqlo&0x7)) * filtmul;
if (filter.freq>quickfloat_ConvertFromInt(1)) if (filter.freq>quickfloat_ConvertFromInt(1))
filter.freq=quickfloat_ConvertFromInt(1); filter.freq=quickfloat_ConvertFromInt(1);
/* the above line isnt correct at all - the problem is that the filter /* the above line isnt correct at all - the problem is that the filter
@ -404,20 +410,20 @@ void synth_render (int32_t *buffer, unsigned long len)
filter.h_ena = get_bit(sid.ftp_vol,6); filter.h_ena = get_bit(sid.ftp_vol,6);
filter.v3ena = !get_bit(sid.ftp_vol,7); filter.v3ena = !get_bit(sid.ftp_vol,7);
filter.vol = (sid.ftp_vol & 0xf); filter.vol = (sid.ftp_vol & 0xf);
filter.rez = quickfloat_ConvertFromFloat(1.2f) - filter.rez = quickfloat_ConvertFromFloat(1.2f) -
quickfloat_ConvertFromFloat(0.04f)*(sid.res_ftv >> 4); quickfloat_ConvertFromFloat(0.04f)*(sid.res_ftv >> 4);
/* We precalculate part of the quick float operation, saves time in loop later */ /* We precalculate part of the quick float operation, saves time in loop later */
filter.rez>>=8; filter.rez>>=8;
#endif #endif
/* now render the buffer */ /* now render the buffer */
for (bp=0;bp<len;bp++) { for (bp=0;bp<len;bp++) {
#ifdef USE_FILTER #ifdef USE_FILTER
int outo=0; int outo[2]={0,0};
#endif #endif
int outf=0; int outf[2]={0,0};
/* step 2 : generate the two output signals (for filtered and non- /* step 2 : generate the two output signals (for filtered and non-
filtered) from the osc/eg sections */ filtered) from the osc/eg sections */
for (v=0;v<3;v++) { for (v=0;v<3;v++) {
@ -473,7 +479,7 @@ void synth_render (int32_t *buffer, unsigned long len)
if (osc[v].wave & 0x20) outv &= sawout; if (osc[v].wave & 0x20) outv &= sawout;
if (osc[v].wave & 0x40) outv &= plsout; if (osc[v].wave & 0x40) outv &= plsout;
if (osc[v].wave & 0x80) outv &= nseout; if (osc[v].wave & 0x80) outv &= nseout;
/* so now process the volume according to the phase and adsr values */ /* so now process the volume according to the phase and adsr values */
switch (osc[v].envphase) { switch (osc[v].envphase) {
case 0 : { /* Phase 0 : Attack */ case 0 : { /* Phase 0 : Attack */
@ -515,25 +521,24 @@ void synth_render (int32_t *buffer, unsigned long len)
break; break;
} }
} }
#ifdef USE_FILTER #ifdef USE_FILTER
/* now route the voice output to either the non-filtered or the /* now route the voice output to either the non-filtered or the
filtered channel and dont forget to blank out osc3 if desired */ filtered channel and dont forget to blank out osc3 if desired */
if (v<2 || filter.v3ena) if (v<2 || filter.v3ena)
{ {
if (osc[v].filter) if (osc[v].filter)
outf+=(((int)(outv-0x80))*osc[v].envval)>>22; outf[v&1]+=(((int)(outv-0x80))*osc[v].envval)>>22;
else else
outo+=(((int)(outv-0x80))*osc[v].envval)>>22; outo[v&1]+=(((int)(outv-0x80))*osc[v].envval)>>22;
} }
#else /* !USE_FILTER */
/* Don't use filters, just mix voices together */
outf[v&1]+=((signed short)(outv-0x80)) * (osc[v].envval>>4);
#endif #endif
#ifndef USE_FILTER } /* for (v=0;v<3;v++) */
/* Don't use filters, just mix all voices together */
outf+=((signed short)(outv-0x80)) * (osc[v].envval>>4);
#endif
}
#ifdef USE_FILTER #ifdef USE_FILTER
@ -549,23 +554,29 @@ void synth_render (int32_t *buffer, unsigned long len)
* This filter sounds a lot like the 8580, as the low-quality, dirty * This filter sounds a lot like the 8580, as the low-quality, dirty
* sound of the 6581 is uuh too hard to achieve :) */ * sound of the 6581 is uuh too hard to achieve :) */
filter.h = quickfloat_ConvertFromInt(outf) - (filter.b>>8)*filter.rez - filter.l; outf[0]+=outf[2]; /* mix voice 1 and 3 to right channel */
filter.b += quickfloat_Multiply(filter.freq, filter.h); for (v=0;v<2;v++) { /* do step 3 for both channels */
filter.l += quickfloat_Multiply(filter.freq, filter.b); filter.h = quickfloat_ConvertFromInt(outf[v]) - (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; outf[v] = 0;
if (filter.l_ena) outf+=quickfloat_ConvertToInt(filter.l); if (filter.l_ena) outf[v]+=quickfloat_ConvertToInt(filter.l);
if (filter.b_ena) outf+=quickfloat_ConvertToInt(filter.b); if (filter.b_ena) outf[v]+=quickfloat_ConvertToInt(filter.b);
if (filter.h_ena) outf+=quickfloat_ConvertToInt(filter.h); if (filter.h_ena) outf[v]+=quickfloat_ConvertToInt(filter.h);
}
int final_sample = (filter.vol*(outo+outf)); /* mix in other channel to reduce stereo panning for better sound on headphones */
*(buffer+bp)= GenerateDigi(final_sample)<<13; int final_sample_r = filter.vol*((outo[0]+outf[0]) + ((outo[1]+outf[1])>>4));
int final_sample_l = filter.vol*((outo[1]+outf[1]) + ((outo[0]+outf[0])>>4));
*(buffer_r+bp)= GenerateDigi(final_sample_r)<<13;
*(buffer_l+bp)= GenerateDigi(final_sample_l)<<13;
#else /* !USE_FILTER */
*(buffer_r+bp) = GenerateDigi(outf[0])<<3;
*(buffer_l+bp) = GenerateDigi(outf[1])<<3;
#endif #endif
#ifndef USE_FILTER } /*for (bp=0;bp<len;bp++) */
*(buffer+bp) = GenerateDigi(outf)<<3;
#endif
}
} }
@ -574,18 +585,18 @@ void synth_render (int32_t *buffer, unsigned long len)
* C64 Mem Routines * C64 Mem Routines
*/ */
static inline unsigned char getmem(unsigned short addr) static inline unsigned char getmem(unsigned short addr)
{ {
return memory[addr]; return memory[addr];
} }
static inline void setmem(unsigned short addr, unsigned char value) static inline void setmem(unsigned short addr, unsigned char value)
{ {
if ((addr&0xfc00)==0xd400) if ((addr&0xfc00)==0xd400)
{ {
sidPoke(addr&0x1f,value); sidPoke(addr&0x1f,value);
/* New SID-Register */ /* New SID-Register */
if (addr > 0xd418) if (addr > 0xd418)
{ {
switch (addr) switch (addr)
{ {
case 0xd41f: /* Start-Hi */ case 0xd41f: /* Start-Hi */
@ -610,10 +621,10 @@ static inline void setmem(unsigned short addr, unsigned char value)
internal_order = value; break; internal_order = value; break;
case 0xd45f: /* Sample Add */ case 0xd45f: /* Sample Add */
internal_add = value; break; internal_add = value; break;
case 0xd41d: /* Start sampling */ case 0xd41d: /* Start sampling */
sample_repeats = internal_repeat_times; sample_repeats = internal_repeat_times;
sample_position = internal_start; sample_position = internal_start;
sample_start = internal_start; sample_start = internal_start;
sample_end = internal_end; sample_end = internal_end;
sample_repeat_start = internal_repeat_start; sample_repeat_start = internal_repeat_start;
sample_period = internal_period; sample_period = internal_period;
@ -621,13 +632,13 @@ static inline void setmem(unsigned short addr, unsigned char value)
switch (value) switch (value)
{ {
case 0xfd: sample_active = 0; break; case 0xfd: sample_active = 0; break;
case 0xfe: case 0xfe:
case 0xff: sample_active = 1; break; case 0xff: sample_active = 1; break;
default: return; default: return;
} }
break; break;
} }
} }
} }
else memory[addr]=value; else memory[addr]=value;
} }
@ -660,10 +671,10 @@ void sidPoke(int reg, unsigned char val)
sid.v[voice].pulse = (sid.v[voice].pulse&0xff)+(val<<8); sid.v[voice].pulse = (sid.v[voice].pulse&0xff)+(val<<8);
break; break;
} }
case 4: { sid.v[voice].wave = val; case 4: { sid.v[voice].wave = val;
/* Directly look at GATE-Bit! /* Directly look at GATE-Bit!
* a change may happen twice or more often during one cpujsr * a change may happen twice or more often during one cpujsr
* Put the Envelope Generator into attack or release phase if desired * Put the Envelope Generator into attack or release phase if desired
*/ */
if ((val & 0x01) == 0) osc[voice].envphase=3; if ((val & 0x01) == 0) osc[voice].envphase=3;
else if (osc[voice].envphase==3) osc[voice].envphase=0; else if (osc[voice].envphase==3) osc[voice].envphase=0;
@ -683,7 +694,7 @@ void sidPoke(int reg, unsigned char val)
static inline unsigned char getaddr(int mode) static inline unsigned char getaddr(int mode)
{ {
unsigned short ad,ad2; unsigned short ad,ad2;
switch(mode) switch(mode)
{ {
case imp: case imp:
@ -702,7 +713,7 @@ static inline unsigned char getaddr(int mode)
case absy: case absy:
ad=getmem(pc++); ad=getmem(pc++);
ad|=256*getmem(pc++); ad|=256*getmem(pc++);
ad2=ad+y; ad2=ad+y;
return getmem(ad2); return getmem(ad2);
case zp: case zp:
ad=getmem(pc++); ad=getmem(pc++);
@ -726,11 +737,11 @@ static inline unsigned char getaddr(int mode)
ad=getmem(pc++); ad=getmem(pc++);
ad2=getmem(ad); ad2=getmem(ad);
ad2|=getmem((ad+1)&0xff)<<8; ad2|=getmem((ad+1)&0xff)<<8;
ad=ad2+y; ad=ad2+y;
return getmem(ad); return getmem(ad);
case acc: case acc:
return a; return a;
} }
return 0; return 0;
} }
@ -747,7 +758,7 @@ static inline void setaddr(int mode, unsigned char val)
case absx: case absx:
ad=getmem(pc-2); ad=getmem(pc-2);
ad|=256*getmem(pc-1); ad|=256*getmem(pc-1);
ad2=ad+x; ad2=ad+x;
setmem(ad2,val); setmem(ad2,val);
return; return;
case zp: case zp:
@ -785,7 +796,7 @@ static inline void putaddr(int mode, unsigned char val)
case absy: case absy:
ad=getmem(pc++); ad=getmem(pc++);
ad|=getmem(pc++)<<8; ad|=getmem(pc++)<<8;
ad2=ad+y; ad2=ad+y;
setmem(ad2,val); setmem(ad2,val);
return; return;
case zp: case zp:
@ -810,7 +821,7 @@ static inline void putaddr(int mode, unsigned char val)
ad2|=getmem(ad&0xff)<<8; ad2|=getmem(ad&0xff)<<8;
setmem(ad2,val); setmem(ad2,val);
return; return;
case indy: case indy:
ad=getmem(pc++); ad=getmem(pc++);
ad2=getmem(ad); ad2=getmem(ad);
ad2|=getmem((ad+1)&0xff)<<8; ad2|=getmem((ad+1)&0xff)<<8;
@ -856,8 +867,8 @@ void cpuReset(void)
{ {
a=x=y=0; a=x=y=0;
p=0; p=0;
s=255; s=255;
pc=getaddr(0xfffc); pc=getaddr(0xfffc);
} }
void cpuResetTo(unsigned short npc, unsigned char na) ICODE_ATTR; void cpuResetTo(unsigned short npc, unsigned char na) ICODE_ATTR;
@ -868,7 +879,7 @@ void cpuResetTo(unsigned short npc, unsigned char na)
y=0; y=0;
p=0; p=0;
s=255; s=255;
pc=npc; pc=npc;
} }
static inline void cpuParse(void) static inline void cpuParse(void)
@ -876,7 +887,7 @@ static inline void cpuParse(void)
unsigned char opc=getmem(pc++); unsigned char opc=getmem(pc++);
int cmd=opcodes[opc]; int cmd=opcodes[opc];
int addr=modes[opc]; int addr=modes[opc];
int c; int c;
switch (cmd) switch (cmd)
{ {
case adc: case adc:
@ -957,14 +968,14 @@ static inline void cpuParse(void)
bval=getaddr(addr); bval=getaddr(addr);
wval=(unsigned short)x-bval; wval=(unsigned short)x-bval;
setflags(FLAG_Z,!wval); setflags(FLAG_Z,!wval);
setflags(FLAG_N,wval&0x80); setflags(FLAG_N,wval&0x80);
setflags(FLAG_C,x>=bval); setflags(FLAG_C,x>=bval);
break; break;
case cpy: case cpy:
bval=getaddr(addr); bval=getaddr(addr);
wval=(unsigned short)y-bval; wval=(unsigned short)y-bval;
setflags(FLAG_Z,!wval); setflags(FLAG_Z,!wval);
setflags(FLAG_N,wval&0x80); setflags(FLAG_N,wval&0x80);
setflags(FLAG_C,y>=bval); setflags(FLAG_C,y>=bval);
break; break;
case dec: case dec:
@ -1043,7 +1054,7 @@ static inline void cpuParse(void)
setflags(FLAG_Z,!y); setflags(FLAG_Z,!y);
setflags(FLAG_N,y&0x80); setflags(FLAG_N,y&0x80);
break; break;
case lsr: case lsr:
bval=getaddr(addr); wval=(unsigned char)bval; bval=getaddr(addr); wval=(unsigned char)bval;
wval>>=1; wval>>=1;
setaddr(addr,(unsigned char)wval); setaddr(addr,(unsigned char)wval);
@ -1100,7 +1111,7 @@ static inline void cpuParse(void)
wval|=pop()<<8; wval|=pop()<<8;
pc=wval+1; pc=wval+1;
break; break;
case sbc: case sbc:
bval=getaddr(addr)^0xff; bval=getaddr(addr)^0xff;
wval=(unsigned short)a+bval+((p&FLAG_C)?1:0); wval=(unsigned short)a+bval+((p&FLAG_C)?1:0);
setflags(FLAG_C, wval&0x100); setflags(FLAG_C, wval&0x100);
@ -1154,13 +1165,13 @@ static inline void cpuParse(void)
a=y; a=y;
setflags(FLAG_Z, !a); setflags(FLAG_Z, !a);
setflags(FLAG_N, a&0x80); setflags(FLAG_N, a&0x80);
break; break;
} }
} }
void cpuJSR(unsigned short npc, unsigned char na) ICODE_ATTR; void cpuJSR(unsigned short npc, unsigned char na) ICODE_ATTR;
void cpuJSR(unsigned short npc, unsigned char na) void cpuJSR(unsigned short npc, unsigned char na)
{ {
a=na; a=na;
x=0; x=0;
y=0; y=0;
@ -1172,16 +1183,16 @@ void cpuJSR(unsigned short npc, unsigned char na)
while (pc > 1) while (pc > 1)
cpuParse(); cpuParse();
} }
void c64Init(int nSampleRate) ICODE_ATTR; void c64Init(int nSampleRate) ICODE_ATTR;
void c64Init(int nSampleRate) void c64Init(int nSampleRate)
{ {
synth_init(nSampleRate); synth_init(nSampleRate);
memset(memory, 0, sizeof(memory)); memset(memory, 0, sizeof(memory));
cpuReset(); cpuReset();
} }
@ -1211,12 +1222,12 @@ unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr,
*load_addr = pData[data_file_offset]; *load_addr = pData[data_file_offset];
*load_addr|= pData[data_file_offset+1]<<8; *load_addr|= pData[data_file_offset+1]<<8;
*speed = pData[0x15]; *speed = pData[0x15];
memset(memory, 0, sizeof(memory)); memset(memory, 0, sizeof(memory));
memcpy(&memory[*load_addr], &pData[data_file_offset+2], size-(data_file_offset+2)); memcpy(&memory[*load_addr], &pData[data_file_offset+2], size-(data_file_offset+2));
if (*play_addr == 0) if (*play_addr == 0)
{ {
cpuJSR(*init_addr, 0); cpuJSR(*init_addr, 0);
@ -1238,8 +1249,8 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE); ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
/* Sample depth is 28 bit host endian */ /* Sample depth is 28 bit host endian */
ci->configure(DSP_SET_SAMPLE_DEPTH, 28); ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
/* Mono output */ /* Stereo output */
ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
} }
return CODEC_OK; return CODEC_OK;
@ -1260,7 +1271,7 @@ enum codec_status codec_run(void)
} }
codec_set_replaygain(ci->id3); codec_set_replaygain(ci->id3);
/* Load SID file the read_filebuf callback will return the full requested /* Load SID file the read_filebuf callback will return the full requested
* size if at all possible, so there is no need to loop */ * size if at all possible, so there is no need to loop */
ci->seek_buffer(0); ci->seek_buffer(0);
@ -1269,13 +1280,13 @@ enum codec_status codec_run(void)
if (filesize == 0) { if (filesize == 0) {
return CODEC_ERROR; return CODEC_ERROR;
} }
param = ci->id3->elapsed; param = ci->id3->elapsed;
resume = param != 0; resume = param != 0;
goto sid_start; goto sid_start;
/* The main decoder loop */ /* The main decoder loop */
while (1) { while (1) {
long action = ci->get_command(&param); long action = ci->get_command(&param);
@ -1291,7 +1302,7 @@ enum codec_status codec_run(void)
LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr,
&subSongsMax, &subSong, &song_speed, &subSongsMax, &subSong, &song_speed,
(unsigned short)filesize); (unsigned short)filesize);
sidPoke(24, 15); /* Turn on full volume */ sidPoke(24, 15); /* Turn on full volume */
if (!resume || (resume && param)) if (!resume || (resume && param))
subSong = param / 1000; /* Now use the current seek time in subSong = param / 1000; /* Now use the current seek time in
seconds as subsong */ seconds as subsong */
@ -1304,38 +1315,38 @@ enum codec_status codec_run(void)
resume = false; resume = false;
} }
nSamplesRendered = 0; nSamplesRendered = 0;
while (nSamplesRendered < CHUNK_SIZE) while (nSamplesRendered < CHUNK_SIZE)
{ {
if (nSamplesToRender == 0) if (nSamplesToRender == 0)
{ {
cpuJSR(play_addr, 0); cpuJSR(play_addr, 0);
/* Find out if cia timing is used and how many samples /* Find out if cia timing is used and how many samples
have to be calculated for each cpujsr */ have to be calculated for each cpujsr */
int nRefreshCIA = (int)(20000*(memory[0xdc04]|(memory[0xdc05]<<8))/0x4c00); int nRefreshCIA = (int)(20000*(memory[0xdc04]|(memory[0xdc05]<<8))/0x4c00);
if ((nRefreshCIA==0) || (song_speed == 0)) if ((nRefreshCIA==0) || (song_speed == 0))
nRefreshCIA = 20000; nRefreshCIA = 20000;
nSamplesPerCall = mixing_frequency*nRefreshCIA/1000000; nSamplesPerCall = mixing_frequency*nRefreshCIA/1000000;
nSamplesToRender = nSamplesPerCall; nSamplesToRender = nSamplesPerCall;
} }
if (nSamplesRendered + nSamplesToRender > CHUNK_SIZE) if (nSamplesRendered + nSamplesToRender > CHUNK_SIZE)
{ {
synth_render(samples+nSamplesRendered, CHUNK_SIZE-nSamplesRendered); synth_render(samples_r+nSamplesRendered, samples_l+nSamplesRendered, CHUNK_SIZE-nSamplesRendered);
nSamplesToRender -= CHUNK_SIZE-nSamplesRendered; nSamplesToRender -= CHUNK_SIZE-nSamplesRendered;
nSamplesRendered = CHUNK_SIZE; nSamplesRendered = CHUNK_SIZE;
} }
else else
{ {
synth_render(samples+nSamplesRendered, nSamplesToRender); synth_render(samples_r+nSamplesRendered, samples_l+nSamplesRendered, nSamplesToRender);
nSamplesRendered += nSamplesToRender; nSamplesRendered += nSamplesToRender;
nSamplesToRender = 0; nSamplesToRender = 0;
} }
} }
ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE); ci->pcmbuf_insert(samples_r, samples_l, CHUNK_SIZE);
} }
return CODEC_OK; return CODEC_OK;