release r2, update credits
[fceu.git] / sound.c
CommitLineData
c62d2810 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21/********************************************************/
22/******* sound.c */
23/******* */
24/******* Sound emulation code and waveform synthesis */
25/******* routines. A few ideas were inspired */
26/******* by code from Marat Fayzullin's EMUlib */
27/******* */
9115e7d2 28/********************************************************/
c62d2810 29
30#include <stdlib.h>
31#include <stdio.h>
32
33#include <string.h>
34
35#include "types.h"
36#include "x6502.h"
37
38#include "fce.h"
39#include "svga.h"
40#include "sound.h"
5bd16b94 41#include "state.h"
c62d2810 42
a384bf44 43uint32 Wave[2048+512];
ebde7d27 44int32 WaveHi[40000]; // unused
a384bf44 45int16 WaveFinalMono[2048+512];
c62d2810 46
a384bf44 47EXPSOUND GameExpSound={0,0,0,0,0,0};
c62d2810 48
49uint8 trimode=0;
50uint8 tricoop=0;
5bd16b94 51
52static uint8 IRQFrameMode=0; /* $4017 / xx000000 */
c62d2810 53uint8 PSG[0x18];
5bd16b94 54static uint8 RawDALatch=0; /* $4011 0xxxxxxx */
55
56uint8 EnabledChannels=0; /* Byte written to $4015 */
c62d2810 57
58uint8 decvolume[3];
59uint8 realvolume[3];
60
61static int32 count[5];
ec4d13a3 62static int32 sqacc[2]={0,0};
c62d2810 63uint8 sqnon=0;
64
4fdfab07 65uint32 soundtsoffs=0;
66
c62d2810 67#undef printf
68uint16 nreg;
9115e7d2 69
a384bf44 70static int32 lengthcount[4];
c62d2810 71
5232c20c 72extern int soundvol;
73
c62d2810 74static const uint8 Slengthtable[0x20]=
75{
76 0x5,0x7f,0xA,0x1,0x14,0x2,0x28,0x3,0x50,0x4,0x1E,0x5,0x7,0x6,0x0E,0x7,
77 0x6,0x08,0xC,0x9,0x18,0xa,0x30,0xb,0x60,0xc,0x24,0xd,0x8,0xe,0x10,0xf
78};
79
80static uint32 lengthtable[0x20];
81
82static const uint32 SNoiseFreqTable[0x10]=
83{
84 2,4,8,0x10,0x20,0x30,0x40,0x50,0x65,0x7f,0xbe,0xfe,0x17d,0x1fc,0x3f9,0x7f2
85};
86static uint32 NoiseFreqTable[0x10];
87
a384bf44 88int32 nesincsize;
89uint32 soundtsinc;
90uint32 soundtsi;
91
c62d2810 92
93static const uint8 NTSCPCMTable[0x10]=
94{
95 0xd6,0xbe,0xaa,0xa0,0x8f,0x7f,0x71,0x6b,
96 0x5f,0x50,0x47,0x40,0x35,0x2a,0x24,0x1b
97};
98
99static const uint8 PALPCMTable[0x10]= // These values are just guessed.
100{
101 0xc6,0xb0,0x9d,0x94,0x84,0x75,0x68,0x63,
102 0x58,0x4a,0x41,0x3b,0x31,0x27,0x21,0x19
103};
104
5bd16b94 105static const uint32 NTSCDMCTable[0x10]=
106{
107 428,380,340,320,286,254,226,214,
108 190,160,142,128,106, 84 ,72,54
109};
110
111/* Previous values for PAL DMC was value - 1,
112 * I am not certain if this is if FCEU handled
113 * PAL differently or not, the NTSC values are right,
114 * so I am assuming that the current value is handled
115 * the same way NTSC is handled. */
116
117static const uint32 PALDMCTable[0x10]=
118{
119 398, 354, 316, 298, 276, 236, 210, 198,
120 176, 148, 132, 118, 98, 78, 66, 50
121};
c62d2810 122
123// $4010 - Frequency
124// $4011 - Actual data outputted
125// $4012 - Address register: $c000 + V*64
126// $4013 - Size register: Size in bytes = (V+1)*64
127
5bd16b94 128int32 DMCacc=1;
129int32 DMCPeriod=0;
130uint8 DMCBitCount=0;
131
132uint8 DMCAddressLatch=0,DMCSizeLatch=0; /* writes to 4012 and 4013 */
133uint8 DMCFormat=0; /* Write to $4010 */
134
135static uint32 DMCAddress=0;
136static int32 DMCSize=0;
137static uint8 DMCShift=0;
138static uint8 SIRQStat=0;
c62d2810 139
5bd16b94 140static char DMCHaveDMA=0;
141static uint8 DMCDMABuf=0;
142char DMCHaveSample=0;
143
144uint32 PSG_base;
c62d2810 145
d447f17f 146static void Dummyfunc(int end) {};
c62d2810 147
d447f17f 148static void (*DoNoise)(int end)=Dummyfunc;
149static void (*DoTriangle)(int end)=Dummyfunc;
150static void (*DoPCM)(int end)=Dummyfunc;
151static void (*DoSQ1)(int end)=Dummyfunc;
152static void (*DoSQ2)(int end)=Dummyfunc;
c62d2810 153
c62d2810 154uint8 sweepon[2]={0,0};
155int32 curfreq[2]={0,0};
156
c62d2810 157uint8 SweepCount[2];
158uint8 DecCountTo1[3];
159
160uint8 fcnt=0;
161int32 fhcnt=0;
162int32 fhinc;
163
164static uint8 laster;
165
166/* Instantaneous? Maybe the new freq value is being calculated all of the time... */
167static int FASTAPASS(2) CheckFreq(uint32 cf, uint8 sr)
168{
169 uint32 mod;
170 if(!(sr&0x8))
171 {
172 mod=cf>>(sr&7);
173 if((mod+cf)&0x800)
174 return(0);
175 }
176 return(1);
177}
178
5bd16b94 179static uint8 DutyCount[2]={0,0};
180
181static void LoadDMCPeriod(uint8 V)
c62d2810 182{
5bd16b94 183 if(PAL)
184 DMCPeriod=PALDMCTable[V];
185 else
186 DMCPeriod=NTSCDMCTable[V];
c62d2810 187}
188
5bd16b94 189static void PrepDPCM()
190{
191 DMCAddress=0x4000+(DMCAddressLatch<<6);
192 DMCSize=(DMCSizeLatch<<4)+1;
193}
194
195static void SQReload(int x, uint8 V)
196{
197 if(EnabledChannels&(1<<x))
198 {
199 if(x)
200 DoSQ2(0);
201 else
202 DoSQ1(0);
203 lengthcount[x]=lengthtable[(V>>3)&0x1f];
204 sqnon|=1<<x;
205 }
206
207 sweepon[x]=PSG[(x<<2)|1]&0x80;
208 curfreq[x]=PSG[(x<<2)|0x2]|((V&7)<<8);
209 decvolume[x]=0xF;
210 SweepCount[x]=((PSG[(x<<2)|0x1]>>4)&7)+1;
211 DutyCount[x]=0;
212 sqacc[x]=((int32)curfreq[0]+1)<<18;
213
214 //RectDutyCount[x]=7;
215 //EnvUnits[x].reloaddec=1;
216 //reloadfreq[x]=1;
217}
c62d2810 218
219static DECLFW(Write_PSG)
220{
221 //if((A>=0x4004 && A<=0x4007) || A==0x4015)
4fdfab07 222 //printf("$%04x:$%02x, %d\n",A,V,SOUNDTS);
c62d2810 223 A&=0x1f;
224 switch(A)
225 {
226 case 0x0:
d447f17f 227 DoSQ1(0);
c62d2810 228 if(V&0x10)
229 realvolume[0]=V&0xF;
230 break;
231 case 0x1:
232 sweepon[0]=V&0x80;
233 break;
234 case 0x2:
d447f17f 235 DoSQ1(0);
c62d2810 236 curfreq[0]&=0xFF00;
237 curfreq[0]|=V;
238 break;
9115e7d2 239 case 0x3:
5bd16b94 240 SQReload(0,V);
c62d2810 241 break;
242
9115e7d2 243 case 0x4:
d447f17f 244 DoSQ2(0);
c62d2810 245 if(V&0x10)
246 realvolume[1]=V&0xF;
247 break;
248 case 0x5:
249 sweepon[1]=V&0x80;
250 break;
251 case 0x6:
d447f17f 252 DoSQ2(0);
c62d2810 253 curfreq[1]&=0xFF00;
254 curfreq[1]|=V;
255 break;
9115e7d2 256 case 0x7:
5bd16b94 257 SQReload(1,V);
c62d2810 258 break;
9115e7d2 259 case 0x8:
d447f17f 260 DoTriangle(0);
c62d2810 261 if(laster&0x80)
262 {
263 tricoop=V&0x7F;
264 trimode=V&0x80;
265 }
266 if(!(V&0x7F))
267 tricoop=0;
268 laster=V&0x80;
269 break;
d447f17f 270 case 0xa:DoTriangle(0);
c62d2810 271 break;
272 case 0xb:
5bd16b94 273 if(EnabledChannels&0x4)
c62d2810 274 {
d447f17f 275 DoTriangle(0);
c62d2810 276 sqnon|=4;
277 lengthcount[2]=lengthtable[(V>>3)&0x1f];
278 }
279 laster=0x80;
280 tricoop=PSG[0x8]&0x7f;
281 trimode=PSG[0x8]&0x80;
282 break;
d447f17f 283 case 0xC:DoNoise(0);
c62d2810 284 if(V&0x10)
285 realvolume[2]=V&0xF;
286 break;
d447f17f 287 case 0xE:DoNoise(0);break;
c62d2810 288 case 0xF:
5bd16b94 289 if(EnabledChannels&8)
c62d2810 290 {
d447f17f 291 DoNoise(0);
c62d2810 292 sqnon|=8;
293 lengthcount[3]=lengthtable[(V>>3)&0x1f];
294 }
295 decvolume[2]=0xF;
9115e7d2 296 DecCountTo1[2]=(PSG[0xC]&0xF)+1;
c62d2810 297 break;
5bd16b94 298 }
299 PSG[A]=V;
300}
c62d2810 301
5bd16b94 302static DECLFW(Write_DMCRegs)
303{
304 A&=0xF;
305
306 switch(A)
307 {
308 case 0x00:DoPCM(0);
309 LoadDMCPeriod(V&0xF);
310
311 if(SIRQStat&0x80)
c62d2810 312 {
5bd16b94 313 if(!(V&0x80))
c62d2810 314 {
5bd16b94 315 X6502_IRQEnd(FCEU_IQDPCM);
316 SIRQStat&=~0x80;
c62d2810 317 }
5bd16b94 318 else X6502_IRQBegin(FCEU_IQDPCM);
c62d2810 319 }
5bd16b94 320 DMCFormat=V;
321 break;
322 case 0x01:DoPCM(0);
323 RawDALatch=V&0x7F;
324 break;
325 case 0x02:DMCAddressLatch=V;break;
326 case 0x03:DMCSizeLatch=V;break;
c62d2810 327 }
c62d2810 328}
329
5bd16b94 330static DECLFW(StatusWrite)
c62d2810 331{
5bd16b94 332 int x;
333 int32 end=(SOUNDTS<<16)/soundtsinc;
334 int t=V^EnabledChannels;
335
336 if(t&1)
337 DoSQ1(end);
338 if(t&2)
339 DoSQ2(end);
340 if(t&4)
341 DoTriangle(end);
342 if(t&8)
343 DoNoise(end);
344 if(t&0x10)
345 DoPCM(end);
346 sqnon&=V;
347 for(x=0;x<4;x++)
348 if(!(V&(1<<x))) lengthcount[x]=0; /* Force length counters to 0. */
349
350 if(V&0x10)
351 {
352 if(!DMCSize)
353 PrepDPCM();
354 }
355 else
356 {
357 DMCSize=0;
358 }
359 SIRQStat&=~0x80;
360 X6502_IRQEnd(FCEU_IQDPCM);
361 EnabledChannels=V&0x1F;
c62d2810 362}
363
5bd16b94 364DECLFR(StatusRead)
c62d2810 365{
5bd16b94 366 //int x;
c62d2810 367 uint8 ret;
368
5bd16b94 369 ret=SIRQStat;
370
371 //for(x=0;x<4;x++) ret|=lengthcount[x]?(1<<x):0;
372 ret|=EnabledChannels&sqnon;
373 if(DMCSize) ret|=0x10;
374
375 #ifdef FCEUDEF_DEBUGGER
376 if(!fceuindbg)
377 #endif
378 {
379 SIRQStat&=~0x40;
380 X6502_IRQEnd(FCEU_IQFCOUNT);
381 }
c62d2810 382 return ret;
383}
384
385static void FASTAPASS(1) FrameSoundStuff(int V)
386{
387 int P;
d447f17f 388 uint32 end = (SOUNDTS<<16)/soundtsinc;
c62d2810 389
d447f17f 390 DoSQ1(end);
391 DoSQ2(end);
392 DoNoise(end);
c62d2810 393
394 switch((V&1))
395 {
396 case 1: /* Envelope decay, linear counter, length counter, freq sweep */
5bd16b94 397 if(EnabledChannels&4 && sqnon&4)
c62d2810 398 if(!(PSG[8]&0x80))
399 {
400 if(lengthcount[2]>0)
401 {
402 lengthcount[2]--;
403 if(lengthcount[2]<=0)
404 {
d447f17f 405 DoTriangle(0);
c62d2810 406 sqnon&=~4;
407 }
9115e7d2 408 }
c62d2810 409 }
410
411 for(P=0;P<2;P++)
412 {
5bd16b94 413 if(EnabledChannels&(P+1) && sqnon&(P+1))
c62d2810 414 {
415 if(!(PSG[P<<2]&0x20))
416 {
417 if(lengthcount[P]>0)
418 {
9115e7d2 419 lengthcount[P]--;
c62d2810 420 if(lengthcount[P]<=0)
421 {
422 sqnon&=~(P+1);
423 }
424 }
425 }
426 }
427 /* Frequency Sweep Code Here */
428 /* xxxx 0000 */
429 /* xxxx = hz. 120/(x+1)*/
430 if(sweepon[P])
431 {
432 int32 mod=0;
433
9115e7d2 434 if(SweepCount[P]>0) SweepCount[P]--;
c62d2810 435 if(SweepCount[P]<=0)
436 {
437 SweepCount[P]=((PSG[(P<<2)+0x1]>>4)&7)+1; //+1;
438 {
439 if(PSG[(P<<2)+0x1]&0x8)
440 {
9115e7d2 441 mod-=(P^1)+((curfreq[P])>>(PSG[(P<<2)+0x1]&7));
c62d2810 442
443 if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/)
444 {
445 curfreq[P]+=mod;
446 }
447 }
448 else
449 {
450 mod=curfreq[P]>>(PSG[(P<<2)+0x1]&7);
451 if((mod+curfreq[P])&0x800)
452 {
453 sweepon[P]=0;
454 curfreq[P]=0;
455 }
456 else
457 {
458 if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/)
459 {
460 curfreq[P]+=mod;
461 }
462 }
463 }
464 }
465 }
9115e7d2 466 }
c62d2810 467 }
468
5bd16b94 469 if(EnabledChannels&0x8 && sqnon&8)
c62d2810 470 {
471 if(!(PSG[0xC]&0x20))
472 {
473 if(lengthcount[3]>0)
474 {
475 lengthcount[3]--;
476 if(lengthcount[3]<=0)
477 {
478 sqnon&=~8;
479 }
480 }
481 }
482 }
483
484 case 0: /* Envelope decay + linear counter */
485 if(!trimode)
9115e7d2 486 {
c62d2810 487 laster=0;
488 if(tricoop)
489 {
d447f17f 490 if(tricoop==1) DoTriangle(0);
c62d2810 491 tricoop--;
492 }
493 }
494
495 for(P=0;P<2;P++)
496 {
497 if(DecCountTo1[P]>0) DecCountTo1[P]--;
498 if(DecCountTo1[P]<=0)
499 {
500 DecCountTo1[P]=(PSG[P<<2]&0xF)+1;
501 if(decvolume[P] || PSG[P<<2]&0x20)
502 {
503 decvolume[P]--;
504 /* Step from 0 to full volume seems to take twice as long
505 as the other steps. I don't know if this is the correct
506 way to double its length, though(or if it even matters).
507 */
508 if((PSG[P<<2]&0x20) && (decvolume[P]==0))
509 DecCountTo1[P]<<=1;
510 decvolume[P]&=15;
511 }
512 }
513 if(!(PSG[P<<2]&0x10))
514 realvolume[P]=decvolume[P];
515 }
516
517 if(DecCountTo1[2]>0) DecCountTo1[2]--;
518 if(DecCountTo1[2]<=0)
519 {
520 DecCountTo1[2]=(PSG[0xC]&0xF)+1;
521 if(decvolume[2] || PSG[0xC]&0x20)
522 {
523 decvolume[2]--;
524 /* Step from 0 to full volume seems to take twice as long
525 as the other steps. I don't know if this is the correct
526 way to double its length, though(or if it even matters).
527 */
528 if((PSG[0xC]&0x20) && (decvolume[2]==0))
529 DecCountTo1[2]<<=1;
530 decvolume[2]&=15;
531 }
532 }
533 if(!(PSG[0xC]&0x10))
534 realvolume[2]=decvolume[2];
535
536 break;
537 }
538
539}
540
541void FrameSoundUpdate(void)
542{
543 // Linear counter: Bit 0-6 of $4008
544 // Length counter: Bit 4-7 of $4003, $4007, $400b, $400f
545
5bd16b94 546 if(!fcnt && !(IRQFrameMode&0x3))
547 {
548 SIRQStat|=0x40;
549 X6502_IRQBegin(FCEU_IQFCOUNT);
550 }
551
c62d2810 552 if(fcnt==3)
553 {
5bd16b94 554 if(IRQFrameMode&0x2)
c62d2810 555 fhcnt+=fhinc;
c62d2810 556 }
c62d2810 557 FrameSoundStuff(fcnt);
558 fcnt=(fcnt+1)&3;
559}
560
5bd16b94 561static INLINE void tester(void)
562{
563 if(DMCBitCount==0)
564 {
565 if(!DMCHaveDMA)
566 DMCHaveSample=0;
567 else
568 {
569 DMCHaveSample=1;
570 DMCShift=DMCDMABuf;
571 DMCHaveDMA=0;
572 }
573 }
574}
575
c62d2810 576static uint32 ChannelBC[5];
577
578static uint32 RectAmp[2][8];
579
580static void FASTAPASS(1) CalcRectAmp(int P)
581{
582 static int tal[4]={1,2,4,6};
583 int V;
584 int x;
585 uint32 *b=RectAmp[P];
586 int m;
587
588 //if(PSG[P<<2]&0x10)
589 V=realvolume[P]<<4;
590 //V=(PSG[P<<2]&15)<<4;
591 //else
592 // V=decvolume[P]<<4;
593 m=tal[(PSG[P<<2]&0xC0)>>6];
594 for(x=0;x<m;x++,b++)
595 *b=0;
596 for(;x<8;x++,b++)
597 *b=V;
598}
599
5bd16b94 600static INLINE void DMCDMA(void)
601{
602 if(DMCSize && !DMCHaveDMA)
603 {
604#if 0
605 X6502_DMR(0x8000+DMCAddress);
606 X6502_DMR(0x8000+DMCAddress);
607 X6502_DMR(0x8000+DMCAddress);
608 DMCDMABuf=X6502_DMR(0x8000+DMCAddress);
609#else
610 X6502_AddCycles(4);
611 DMCDMABuf=X.DB=ARead[0x8000+DMCAddress](0x8000+DMCAddress);
612#endif
613 DMCHaveDMA=1;
614 DMCAddress=(DMCAddress+1)&0x7fff;
615 DMCSize--;
616 if(!DMCSize)
617 {
618 if(DMCFormat&0x40)
619 PrepDPCM();
620 else
621 {
622 SIRQStat|=0x80;
623 if(DMCFormat&0x80)
624 X6502_IRQBegin(FCEU_IQDPCM);
625 }
626 }
627 }
628}
629
3ac1cc0b 630void FCEU_SoundCPUHook(int cycles48)
631{
632 fhcnt-=cycles48;
633 if(fhcnt<=0)
634 {
635 FrameSoundUpdate();
636 fhcnt+=fhinc;
637 }
638
5bd16b94 639 DMCDMA();
640 DMCacc-=cycles48/48;
641
642 while(DMCacc<=0)
3ac1cc0b 643 {
5bd16b94 644 if(DMCHaveSample)
3ac1cc0b 645 {
5bd16b94 646 uint8 bah=RawDALatch;
647 int t=((DMCShift&1)<<2)-2;
648
649 /* Unbelievably ugly hack */
650 if(FSettings.SndRate)
3ac1cc0b 651 {
5bd16b94 652 soundtsoffs+=DMCacc;
653 DoPCM(0);
654 soundtsoffs-=DMCacc;
3ac1cc0b 655 }
5bd16b94 656 RawDALatch+=t;
657 if(RawDALatch&0x80)
658 RawDALatch=bah;
3ac1cc0b 659 }
5bd16b94 660
661 DMCacc+=DMCPeriod;
662 DMCBitCount=(DMCBitCount+1)&7;
663 DMCShift>>=1;
664 tester();
3ac1cc0b 665 }
666}
667
d447f17f 668static void RDoPCM(int32 end)
c62d2810 669{
5bd16b94 670 int32 V;
671 int32 start;
c62d2810 672
5bd16b94 673 start=ChannelBC[4];
674 if(end==0) end=(SOUNDTS<<16)/soundtsinc;
675 if(end<=start) return;
676 ChannelBC[4]=end;
c62d2810 677
5bd16b94 678 for(V=start;V<end;V++)
679 //WaveHi[V]+=(((RawDALatch<<16)/256) * FSettings.PCMVolume)&(~0xFFFF); // TODO get rid of floating calculations to binary. set log volume scaling.
680 Wave[V>>4]+=RawDALatch<<3;
c62d2810 681}
682
d447f17f 683static void RDoSQ1(int32 end)
c62d2810 684{
685 int32 V;
d447f17f 686 int32 start;
ec4d13a3 687 int32 freq;
c62d2810 688
c62d2810 689 start=ChannelBC[0];
d447f17f 690 if(end==0) end=(SOUNDTS<<16)/soundtsinc;
c62d2810 691 if(end<=start) return;
692 ChannelBC[0]=end;
693
5bd16b94 694 if(!(EnabledChannels&1 && sqnon&1))
d447f17f 695 return;
696
c62d2810 697 if(curfreq[0]<8 || curfreq[0]>0x7ff)
698 return;
699 if(!CheckFreq(curfreq[0],PSG[0x1]))
700 return;
701
d447f17f 702 CalcRectAmp(0);
703
c62d2810 704 {
705 uint32 out=RectAmp[0][DutyCount[0]];
706 freq=curfreq[0]+1;
707 {
ec4d13a3 708 freq<<=18;
c62d2810 709 for(V=start;V<end;V++)
710 {
711 Wave[V>>4]+=out;
a384bf44 712 sqacc[0]-=nesincsize;
c62d2810 713 if(sqacc[0]<=0)
714 {
715 rea:
716 sqacc[0]+=freq;
717 DutyCount[0]++;
718 if(sqacc[0]<=0) goto rea;
719
720 DutyCount[0]&=7;
721 out=RectAmp[0][DutyCount[0]];
722 }
c62d2810 723 }
c62d2810 724 }
d447f17f 725 }
c62d2810 726}
727
d447f17f 728static void RDoSQ2(int32 end)
c62d2810 729{
730 int32 V;
d447f17f 731 int32 start;
ec4d13a3 732 int32 freq;
c62d2810 733
c62d2810 734 start=ChannelBC[1];
d447f17f 735 if(end==0) end=(SOUNDTS<<16)/soundtsinc;
c62d2810 736 if(end<=start) return;
737 ChannelBC[1]=end;
738
5bd16b94 739 if(!(EnabledChannels&2 && sqnon&2))
d447f17f 740 return;
741
c62d2810 742 if(curfreq[1]<8 || curfreq[1]>0x7ff)
743 return;
744 if(!CheckFreq(curfreq[1],PSG[0x5]))
745 return;
746
d447f17f 747 CalcRectAmp(1);
748
c62d2810 749 {
750 uint32 out=RectAmp[1][DutyCount[1]];
751 freq=curfreq[1]+1;
752
753 {
ec4d13a3 754 freq<<=18;
c62d2810 755 for(V=start;V<end;V++)
756 {
757 Wave[V>>4]+=out;
a384bf44 758 sqacc[1]-=nesincsize;
c62d2810 759 if(sqacc[1]<=0)
760 {
761 rea:
762 sqacc[1]+=freq;
763 DutyCount[1]++;
764 if(sqacc[1]<=0) goto rea;
765
766 DutyCount[1]&=7;
767 out=RectAmp[1][DutyCount[1]];
768 }
c62d2810 769 }
c62d2810 770 }
d447f17f 771 }
c62d2810 772}
773
774
d447f17f 775static void RDoTriangle(int32 end)
c62d2810 776{
777 static uint32 tcout=0;
778 int32 V;
d447f17f 779 int32 start; //,freq;
ec4d13a3 780 int32 freq=(((PSG[0xa]|((PSG[0xb]&7)<<8))+1));
c62d2810 781
782 start=ChannelBC[2];
d447f17f 783 if(end==0) end=(SOUNDTS<<16)/soundtsinc;
c62d2810 784 if(end<=start) return;
785 ChannelBC[2]=end;
786
5bd16b94 787 if(! (EnabledChannels&0x4 && sqnon&4 && tricoop) )
c62d2810 788 { // Counter is halted, but we still need to output.
789 for(V=start;V<end;V++)
790 Wave[V>>4]+=tcout;
791 }
792 else if(freq<=4) // 55.9Khz - Might be barely audible on a real NES, but
793 // it's too costly to generate audio at this high of a frequency
794 // (55.9Khz * 32 for the stepping).
795 // The same could probably be said for ~27.8Khz, so we'll
796 // take care of that too. We'll just output the average
797 // value(15/2 - scaled properly for our output format, of course).
798 // We'll also take care of ~18Khz and ~14Khz too, since they should be barely audible.
799 // (Some proof or anything to confirm/disprove this would be nice.).
800 {
801 for(V=start;V<end;V++)
802 Wave[V>>4]+=((0xF<<4)+(0xF<<2))>>1;
803 }
804 else
805 {
ec4d13a3 806 static int32 triacc=0;
9115e7d2 807 static uint8 tc=0;
c62d2810 808
ec4d13a3 809 freq<<=17;
c62d2810 810 for(V=start;V<end;V++)
811 {
a384bf44 812 triacc-=nesincsize;
c62d2810 813 if(triacc<=0)
814 {
815 rea:
816 triacc+=freq; //t;
817 tc=(tc+1)&0x1F;
818 if(triacc<=0) goto rea;
819
820 tcout=(tc&0xF);
821 if(tc&0x10) tcout^=0xF;
822 tcout=(tcout<<4)+(tcout<<2);
823 }
824 Wave[V>>4]+=tcout;
825 }
826 }
827}
828
d447f17f 829static void RDoNoise(int32 end)
c62d2810 830{
831 int32 inc,V;
d447f17f 832 int32 start;
c62d2810 833
834 start=ChannelBC[3];
d447f17f 835 if(end==0) end=(SOUNDTS<<16)/soundtsinc;
c62d2810 836 if(end<=start) return;
837 ChannelBC[3]=end;
838
5bd16b94 839 if(EnabledChannels&0x8 && sqnon&8)
c62d2810 840 {
841 uint32 outo;
842 uint32 amptab[2];
843 uint8 amplitude;
9115e7d2 844
c62d2810 845 amplitude=realvolume[2];
846 //if(PSG[0xC]&0x10)
847 // amplitude=(PSG[0xC]&0xF);
9115e7d2 848 //else
c62d2810 849 // amplitude=decvolume[2]&0xF;
850
9115e7d2 851 inc=NoiseFreqTable[PSG[0xE]&0xF];
c62d2810 852 amptab[0]=((amplitude<<2)+amplitude+amplitude)<<1;
853 amptab[1]=0;
854 outo=amptab[nreg&1];
855
9115e7d2 856 if(amplitude)
c62d2810 857 {
9115e7d2 858 if(PSG[0xE]&0x80) // "short" noise
c62d2810 859 for(V=start;V<end;V++)
9115e7d2 860 {
c62d2810 861 Wave[V>>4]+=outo;
862 if(count[3]>=inc)
9115e7d2 863 {
864 uint8 feedback;
c62d2810 865
866 feedback=((nreg>>8)&1)^((nreg>>14)&1);
867 nreg=(nreg<<1)+feedback;
868 nreg&=0x7fff;
869 outo=amptab[nreg&1];
870 count[3]-=inc;
871 }
872 count[3]+=0x1000;
873 }
874 else
875 for(V=start;V<end;V++)
876 {
877 Wave[V>>4]+=outo;
878 if(count[3]>=inc)
879 {
880 uint8 feedback;
881
882 feedback=((nreg>>13)&1)^((nreg>>14)&1);
883 nreg=(nreg<<1)+feedback;
884 nreg&=0x7fff;
885 outo=amptab[nreg&1];
886 count[3]-=inc;
887 }
888 count[3]+=0x1000;
889 }
890 }
c62d2810 891 }
892}
893
77887306 894DECLFW(Write_IRQFM)
d97315ac 895{
896 V=(V&0xC0)>>6;
897 fcnt=0;
898 if(V&0x2)
899 FrameSoundUpdate();
900 fcnt=1;
901 fhcnt=fhinc;
902 X6502_IRQEnd(FCEU_IQFCOUNT);
903 SIRQStat&=~0x40;
5bd16b94 904 IRQFrameMode=V;
d97315ac 905}
906
c62d2810 907void SetNESSoundMap(void)
9115e7d2 908{
5bd16b94 909 SetWriteHandler(0x4000,0x400F,Write_PSG);
910 SetWriteHandler(0x4010,0x4013,Write_DMCRegs);
d97315ac 911 SetWriteHandler(0x4017,0x4017,Write_IRQFM);
5bd16b94 912
913 SetWriteHandler(0x4015,0x4015,StatusWrite);
914 SetReadHandler(0x4015,0x4015,StatusRead);
c62d2810 915}
916
9115e7d2 917int32 highp; // 0 through 65536, 0 = no high pass, 65536 = max high pass
c62d2810 918
9115e7d2 919int32 lowp; // 0 through 65536, 65536 = max low pass(total attenuation)
c62d2810 920 // 65536 = no low pass
7b356ee3 921static int32 flt_acc=0, flt_acc2=0;
922
d447f17f 923static void FilterSound(uint32 *in, int16 *outMono, int count)
c62d2810 924{
d447f17f 925// static int min=0, max=0;
0ab4d38f 926 int sh=2;
927 if (soundvol < 5) sh += 5 - soundvol;
ec4d13a3 928
929 for(;count;count--,in++,outMono++)
c62d2810 930 {
ec4d13a3 931 int32 diff;
c62d2810 932
7b356ee3 933 diff = *in - flt_acc;
c62d2810 934
7b356ee3 935 flt_acc += (diff*highp)>>16;
936 flt_acc2+= (int32) (((int64)((diff-flt_acc2)*lowp))>>16);
c62d2810 937 *in=0;
9115e7d2 938
0ab4d38f 939 *outMono = flt_acc2*7 >> sh; // * 7 >> 2 = * 1.75
d447f17f 940// if (acc2 < min) { printf("min: %i %04x\n", acc2, acc2); min = acc2; }
941// if (acc2 > max) { printf("max: %i %04x\n", acc2, acc2); max = acc2; }
c62d2810 942 }
943}
944
5232c20c 945
946
d97315ac 947static int32 inbuf=0;
c62d2810 948int FlushEmulateSound(void)
949{
c62d2810 950 int x;
4fdfab07 951 uint32 end;
5232c20c 952
c62d2810 953 if(!timestamp) return(0);
954
5232c20c 955 if(!FSettings.SndRate || (soundvol == 0))
c62d2810 956 {
957 end=0;
958 goto nosoundo;
959 }
960
4fdfab07 961 end=(SOUNDTS<<16)/soundtsinc;
d447f17f 962 DoSQ1(end);
963 DoSQ2(end);
964 DoTriangle(end);
965 DoNoise(end);
966 DoPCM(end);
c62d2810 967
968 if(GameExpSound.Fill)
969 GameExpSound.Fill(end&0xF);
970
d447f17f 971 FilterSound(Wave,WaveFinalMono,end>>4);
c62d2810 972
973 if(end&0xF)
974 Wave[0]=Wave[(end>>4)];
9115e7d2 975 Wave[(end>>4)]=0;
c62d2810 976
977 nosoundo:
978 for(x=0;x<5;x++)
979 ChannelBC[x]=end&0xF;
4fdfab07 980 soundtsoffs=(soundtsinc*(end&0xF))>>16;
d97315ac 981 end>>=4;
982 inbuf=end;
983 return(end);
c62d2810 984}
985
c4980f9e 986int GetSoundBuffer(int16 **W)
c62d2810 987{
c4980f9e 988 *W=WaveFinalMono;
d97315ac 989 return inbuf;
c62d2810 990}
991
5bd16b94 992void FCEUSND_Power(void)
c62d2810 993{
994 int x;
995
996 SetNESSoundMap();
5bd16b94 997 memset(PSG,0x00,sizeof(PSG));
998 FCEUSND_Reset();
c62d2810 999
5bd16b94 1000 memset(Wave,0,sizeof(Wave));
1001 memset(WaveHi,0,sizeof(WaveHi));
1002 //memset(&EnvUnits,0,sizeof(EnvUnits));
1003
1004 for(x=0;x<5;x++)
1005 ChannelBC[x]=0;
4fdfab07 1006 soundtsoffs=0;
5bd16b94 1007 LoadDMCPeriod(DMCFormat&0xF);
c62d2810 1008}
1009
5bd16b94 1010void FCEUSND_Reset(void)
c62d2810 1011{
1012 int x;
1013 for(x=0;x<0x16;x++)
1014 if(x!=1 && x!=5 && x!=0x14) BWrite[0x4000+x](0x4000+x,0);
5bd16b94 1015
1016 IRQFrameMode=0x0;
c62d2810 1017 fhcnt=fhinc;
1018 fcnt=0;
1019 nreg=1;
5bd16b94 1020
1021 DMCHaveDMA=DMCHaveSample=0;
1022 SIRQStat=0x00;
1023
1024 RawDALatch=0x00;
1025 //TriCount=0;
1026 //TriMode=0;
1027 //tristep=0;
1028 EnabledChannels=0;
1029 for(x=0;x<4;x++)
1030 lengthcount[x]=0;
1031
1032 DMCAddressLatch=0;
1033 DMCSizeLatch=0;
1034 DMCFormat=0;
1035 DMCAddress=0;
1036 DMCSize=0;
1037 DMCShift=0;
1038 DMCacc=1;
1039 DMCBitCount=0;
c62d2810 1040}
1041
77887306 1042void SetSoundVariables(void)
c62d2810 1043{
9115e7d2 1044 int x;
c62d2810 1045
1046 fhinc=PAL?16626:14915; // *2 CPU clock rate
1047 fhinc*=24;
1048 for(x=0;x<0x20;x++)
1049 lengthtable[x]=Slengthtable[x]<<1;
1050
1051 if(FSettings.SndRate)
1052 {
1053 DoNoise=RDoNoise;
1054 DoTriangle=RDoTriangle;
1055 DoPCM=RDoPCM;
1056 DoSQ1=RDoSQ1;
1057 DoSQ2=RDoSQ2;
9115e7d2 1058 }
c62d2810 1059 else
1060 {
1061 DoNoise=DoTriangle=DoPCM=DoSQ1=DoSQ2=Dummyfunc;
1062 }
1063
1064 if(!FSettings.SndRate) return;
1065 if(GameExpSound.RChange)
1066 GameExpSound.RChange();
1067
ec4d13a3 1068 // nesincsizeLL=(int64)((int64)562949953421312*(double)(PAL?PAL_CPU:NTSC_CPU)/(FSettings.SndRate OVERSAMPLE));
a384bf44 1069 nesincsize=(int32)(((int64)1<<17)*(double)(PAL?PAL_CPU:NTSC_CPU)/(FSettings.SndRate * 16)); // 308845 - 1832727
c62d2810 1070 PSG_base=(uint32)(PAL?(long double)PAL_CPU/16:(long double)NTSC_CPU/16);
1071
1072 for(x=0;x<0x10;x++)
1073 {
1074 long double z;
1075 z=SNoiseFreqTable[x]<<1;
1076 z=(PAL?PAL_CPU:NTSC_CPU)/z;
1077 z=(long double)((uint32)((FSettings.SndRate OVERSAMPLE)<<12))/z;
1078 NoiseFreqTable[x]=z;
1079 }
1080 soundtsinc=(uint32)((uint64)(PAL?(long double)PAL_CPU*65536:(long double)NTSC_CPU*65536)/(FSettings.SndRate OVERSAMPLE));
d447f17f 1081 memset(Wave,0,sizeof(Wave));
c62d2810 1082 for(x=0;x<5;x++)
1083 ChannelBC[x]=0;
1084 highp=(250<<16)/FSettings.SndRate; // Arbitrary
ec4d13a3 1085 lowp=(25000<<16)/FSettings.SndRate; // Arbitrary
c62d2810 1086
1087 if(highp>(1<<16)) highp=1<<16;
1088 if(lowp>(1<<16)) lowp=1<<16;
7b356ee3 1089
1090 flt_acc=flt_acc2=0;
c62d2810 1091}
1092
1093void FixOldSaveStateSFreq(void)
1094{
1095 int x;
1096 for(x=0;x<2;x++)
1097 {
1098 curfreq[x]=PSG[0x2+(x<<2)]|((PSG[0x3+(x<<2)]&7)<<8);
1099 }
1100}
1101
1102void FCEUI_Sound(int Rate)
1103{
1104 FSettings.SndRate=Rate;
1105 SetSoundVariables();
1106}
1107
1108void FCEUI_SetSoundVolume(uint32 volume)
1109{
92764e62 1110 FSettings.SoundVolume=volume;
c62d2810 1111}
5bd16b94 1112
1113SFORMAT FCEUSND_STATEINFO[]={
1114 { &fhcnt, 4|FCEUSTATE_RLSB,"FHCN"},
1115 { &fcnt, 1, "FCNT"},
1116 { PSG, 0x10, "PSG"},
1117 { &EnabledChannels, 1, "ENCH"},
1118 { &IRQFrameMode, 1, "IQFM"},
1119
1120 { decvolume, 3, "DECV"},
1121 { &sqnon, 1, "SQNO"},
1122 { &nreg, 2|RLSB, "NREG"},
1123 { &trimode, 1, "TRIM"},
1124 { &tricoop, 1, "TRIC"},
1125 { DecCountTo1, 3,"DCT1"},
1126
1127 { sweepon, 2, "SWEE"},
1128 { &curfreq[0], 4|FCEUSTATE_RLSB,"CRF1"},
1129 { &curfreq[1], 4|FCEUSTATE_RLSB,"CRF2"},
1130 { SweepCount, 2,"SWCT"},
1131
1132 { &SIRQStat, 1, "SIRQ"},
1133
1134 { &DMCacc, 4|FCEUSTATE_RLSB, "5ACC"},
1135 { &DMCBitCount, 1, "5BIT"},
1136 { &DMCAddress, 4|FCEUSTATE_RLSB, "5ADD"},
1137 { &DMCSize, 4|FCEUSTATE_RLSB, "5SIZ"},
1138 { &DMCShift, 1, "5SHF"},
1139
1140 { &DMCHaveDMA, 1, "5HVDM"},
1141 { &DMCHaveSample, 1, "5HVSP"},
1142
1143 { &DMCSizeLatch, 1, "5SZL"},
1144 { &DMCAddressLatch, 1, "5ADL"},
1145 { &DMCFormat, 1, "5FMT"},
1146 { &RawDALatch, 1, "RWDA"},
1147 { 0, }
1148};
1149
1150void FCEUSND_SaveState(void)
1151{
1152
1153}
1154
1155void FCEUSND_LoadState(int version)
1156{
1157 LoadDMCPeriod(DMCFormat&0xF);
1158 RawDALatch&=0x7F;
1159 DMCAddress&=0x7FFF;
1160}