Exemplo n.º 1
0
        private int CYCLES_PER_SAMPLE = 0x300; //33868800 / 44100hz
        public bool tick(int cycles)
        {
            counter += cycles;

            if (counter < CYCLES_PER_SAMPLE)
            {
                return(false);
            }
            counter -= CYCLES_PER_SAMPLE;

            if (cdbuffer.Count > 2)
            {
                byte cdLLo = cdbuffer.Dequeue();
                byte cdLHi = cdbuffer.Dequeue();
                byte cdRLo = cdbuffer.Dequeue();
                byte cdRHi = cdbuffer.Dequeue();

                samples.Add(cdLLo);
                samples.Add(cdLHi);
                samples.Add(cdRLo);
                samples.Add(cdRHi);
            }

            if (samples.Count > 2048)
            {
                //Console.WriteLine("spu play!");
                window.Play(samples.ToArray());
                samples.Clear();
            }

            return(false);
        }
Exemplo n.º 2
0
        private int CYCLES_PER_SAMPLE = 0x300; //33868800 / 44100hz
        public bool tick(int cycles)
        {
            bool edgeTrigger = false;

            counter += cycles;

            if (counter < CYCLES_PER_SAMPLE)
            {
                return(false);
            }
            counter -= CYCLES_PER_SAMPLE;

            int sumLeft  = 0;
            int sumRight = 0;

            uint edgeKeyOn  = keyOn;
            uint edgeKeyOff = keyOff;

            keyOn  = 0;
            keyOff = 0;

            tickNoiseGenerator();

            for (int i = 0; i < voices.Length; i++)
            {
                Voice v = voices[i];

                //keyOn and KeyOff are edge triggered on 0 to 1
                if ((edgeKeyOff & (0x1 << i)) != 0)
                {
                    v.keyOff();
                }

                if ((edgeKeyOn & (0x1 << i)) != 0)
                {
                    endx &= ~(uint)(0x1 << i);
                    v.keyOn();
                }

                if (v.adsrPhase == Voice.Phase.Off)
                {
                    v.latest = 0;
                    continue;
                }

                short sample;
                if ((channelNoiseMode & (0x1 << i)) != 0)
                {
                    //Generated by tickNoiseGenerator
                    sample = (short)noiseLevel;
                }
                else
                {
                    sample = sampleVoice(i);
                    //Read irqAddress Irq
                    edgeTrigger |= control.irq9Enabled && v.readRamIrq;
                    v.readRamIrq = false;
                }

                //Handle ADSR Envelope
                sample = (short)((sample * v.adsrVolume) >> 15);
                v.tickAdsr(i);

                //Save sample for possible pitch modulation
                v.latest = sample;

                //Sum each voice sample
                sumLeft  += (sample * v.processVolume(v.volumeLeft)) >> 15;
                sumRight += (sample * v.processVolume(v.volumeRight)) >> 15;
            }

            if (!control.spuUnmuted)   //todo merge this on the for voice loop
            //On mute the spu still ticks but output is 0 for voices (not for cdInput)
            {
                sumLeft  = 0;
                sumRight = 0;
            }

            //Merge in CD audio (CDDA or XA)
            short cdL = 0;
            short cdR = 0;

            if (control.cdAudioEnabled && cdbuffer.Count > 3)  //Be sure theres something on the queue...
            //todo refactor the byte/short queues and casts
            {
                byte cdLLo = cdbuffer.Dequeue();
                byte cdLHi = cdbuffer.Dequeue();
                byte cdRLo = cdbuffer.Dequeue();
                byte cdRHi = cdbuffer.Dequeue();

                cdL = (short)(cdLHi << 8 | cdLLo);
                cdR = (short)(cdRHi << 8 | cdRLo);

                //Apply Spu Cd In (CDDA/XA) Volume
                cdL = (short)((cdL * cdVolumeLeft) >> 15);
                cdR = (short)((cdR * cdVolumeRight) >> 15);

                sumLeft  += cdL;
                sumRight += cdR;
            }

            //Write to capture buffers and check ram irq
            edgeTrigger     |= handleCaptureBuffer(0 * 1024 + captureBufferPos, cdL);
            edgeTrigger     |= handleCaptureBuffer(1 * 1024 + captureBufferPos, cdR);
            edgeTrigger     |= handleCaptureBuffer(2 * 1024 + captureBufferPos, voices[1].latest);
            edgeTrigger     |= handleCaptureBuffer(3 * 1024 + captureBufferPos, voices[3].latest);
            captureBufferPos = (captureBufferPos + 2) & 0x3FF;

            //Clamp sum
            sumLeft  = (Math.Clamp(sumLeft, -0x8000, 0x7FFF) * mainVolumeLeft) >> 15;
            sumRight = (Math.Clamp(sumRight, -0x8000, 0x7FFF) * mainVolumeRight) >> 15;

            //Add to samples bytes to output list //Todo check if is possible to cast directly with Unsafe or memmarshal
            spuOutput.Add((byte)sumLeft);
            spuOutput.Add((byte)(sumLeft >> 8));
            spuOutput.Add((byte)sumRight);
            spuOutput.Add((byte)(sumRight >> 8));

            if (spuOutput.Count > 2048)
            {
                window.Play(spuOutput.ToArray());
                spuOutput.Clear();
            }

            if (control.spuEnabled && control.irq9Enabled && edgeTrigger)
            {
                status.irq9Flag = true;
            }
            return(control.spuEnabled && control.irq9Enabled && edgeTrigger); //todo move spuEnabled outside
        }
Exemplo n.º 3
0
        public bool tick(int cycles)
        {
            counter += cycles;

            if (interruptQueue.Count != 0 && IF == 0)
            {
                if (cdDebug)
                {
                    Console.WriteLine($"[CDROM] Interrupt Queue is size: {interruptQueue.Count} dequeue to IF next Interrupt: {interruptQueue.Peek()}");
                }
                IF |= interruptQueue.Dequeue();
                //edgeTrigger = true;
            }

            if (/*edgeTrigger &&*/ (IF & IE) != 0)
            {
                if (cdDebug)
                {
                    Console.WriteLine($"[CD INT] Triggering {IF:x8}");
                }
                edgeTrigger = false;
                isBusy      = false;
                return(true);
            }

            switch (mode)
            {
            case Mode.Idle:
                if (interruptQueue.Count != 0)       //Await some cycles so interrupts are not triggered instant
                {
                    return(false);
                }
                counter = 0;
                //Console.WriteLine("[CDROM] MODE IDLE");
                break;

            case Mode.Seek:     //Hardcoded seek time...
                if (counter < 100000 || interruptQueue.Count != 0)
                {
                    return(false);
                }
                counter = 0;
                //Console.WriteLine("[CDROM] MODE SEEK");
                mode = Mode.Idle;
                STAT = (byte)(STAT & (~0x40));

                responseBuffer.Enqueue(STAT);
                interruptQueue.Enqueue(0x2);
                break;

            case Mode.Read:
            case Mode.Play:
                if (counter < (33868800 / (isDoubleSpeed ? 150 : 75)) || interruptQueue.Count != 0)
                {
                    return(false);
                }
                counter = 0;

                byte[] rawSector = cd.Read(readLoc++);

                if (cdDebug)
                {
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    Console.WriteLine($"Reading readLoc: {readLoc - 1} seekLoc: {seekLoc} size: {rawSector.Length}");
                    Console.ResetColor();
                }

                if (mode == Mode.Play)
                {
                    window.Play(rawSector);
                    return(false);    //CDDA isn't delivered to CPU and doesn't raise interrupt
                }

                //first 12 are the sync header
                sectorHeader.mm   = rawSector[12];
                sectorHeader.ss   = rawSector[13];
                sectorHeader.ff   = rawSector[14];
                sectorHeader.mode = rawSector[15];

                sectorSubHeader.file       = rawSector[16];
                sectorSubHeader.channel    = rawSector[17];
                sectorSubHeader.subMode    = rawSector[18];
                sectorSubHeader.codingInfo = rawSector[19];

                //if (sectorSubHeader.isVideo) Console.WriteLine("is video");
                //if (sectorSubHeader.isData) Console.WriteLine("is data");
                //if (sectorSubHeader.isAudio) Console.WriteLine("is audio");

                if (isXAADPCM && sectorSubHeader.isForm2)
                {
                    if (sectorSubHeader.isEndOfFile)
                    {
                        if (cdDebug)
                        {
                            Console.WriteLine("[CDROM] XA ON: End of File!");
                        }
                        //is this even needed? There seems to not be an AutoPause flag like on CDDA
                        //RR4, Castlevania and others hang sound here if hard stoped to STAT 0x2
                    }

                    if (sectorSubHeader.isRealTime && sectorSubHeader.isAudio)
                    {
                        if (isXAFilter && (filterFile != sectorSubHeader.file || filterChannel != sectorSubHeader.channel))
                        {
                            if (cdDebug)
                            {
                                Console.WriteLine("[CDROM] XA Filter: file || channel");
                            }
                            return(false);
                        }

                        if (cdDebug)
                        {
                            Console.WriteLine("[CDROM] XA ON: Realtime + Audio");              //todo flag to pass to SPU?
                        }
                        byte[] decodedXaAdpcm = XaAdpcm.Decode(rawSector, sectorSubHeader.codingInfo);
                        window.Play(decodedXaAdpcm);

                        return(false);
                    }
                }

                //If we arived here sector is supposed to be delivered to CPU so slice out sync and header based on flag
                if (!isSectorSizeRAW)
                {
                    rawSector = rawSector.AsSpan().Slice(24, 0x800).ToArray();
                }
                else
                {
                    rawSector = rawSector.AsSpan().Slice(12).ToArray();
                }

                cdBuffer = new Queue <byte>(rawSector);

                responseBuffer.Enqueue(STAT);
                interruptQueue.Enqueue(0x1);

                break;

            case Mode.TOC:
                if (counter < 33868800 / (isDoubleSpeed ? 150 : 75) || interruptQueue.Count != 0)
                {
                    return(false);
                }
                mode = Mode.Idle;
                responseBuffer.Enqueue(STAT);
                interruptQueue.Enqueue(0x2);
                counter = 0;
                break;
            }
            return(false);
        }