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); }
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 }
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); }