⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpusound.pas

📁 一个不出名的GBA模拟器
💻 PAS
📖 第 1 页 / 共 2 页
字号:
//////////////////////////////////////////////////////////////////////

procedure soundSetSound4CR;
var
  r: uint16;
begin
  r := Puint16(@(registers[SOUND4_CR]))^;

  sound4.timed := r and (1 shl 14) <> 0;
  sound4.use7steps := r and SOUND4_7_STEPS <> 0;
  sound4.freq := max((r and 7) shl 1, 1) * (1 shl (6 + (r shr 4) and $F));

  // If this bit is written, reset sound 4
  if r and SOUND_RESTART <> 0 then begin
    sound4.enabled := false;
    soundSetSound4Length;
    sound4.index := 0;
    sound4.envIndex := 0;
    sound4.enabled := true;

    if sound4.use7steps then sound4.lfsr := $7F else sound4.lfsr := $7FFF;
  end;

  Puint16(@(registers[SOUND4_CR]))^ := r and not SOUND_RESTART;
end;

//////////////////////////////////////////////////////////////////////

procedure soundWriteDSoundCR;
var
  cr: uint16;
  temp: integer;
begin
  cr := Puint16(@(registers[SOUND_DSOUND_CR]))^;

  // Set DirectSound A shiznat
  if cr and DSA_RESET <> 0 then ResetTDSoundRecord(soundA);
  temp := (cr shr DSA_VOLUME_SH) and 1;
  if cr and DSA_LEFT <> 0 then soundA.lvol := temp+1 else soundA.lvol := 0;
  if cr and DSA_RIGHT <> 0 then soundA.rvol := temp+1 else soundA.rvol := 0;
  soundA.enabled := soundA.lvol + soundA.rvol <> -2;
  if cr and DSA_TIMER <> 0 then soundA.timer := 1 else soundA.timer := 0;

  // Set DirectSound B shiznat
  if cr and DSB_RESET <> 0 then ResetTDSoundRecord(soundB);
  temp := (cr shr DSB_VOLUME_SH) and 1;
  if cr and DSB_LEFT <> 0 then soundB.lvol := temp+1 else soundB.lvol := 0;
  if cr and DSB_RIGHT <> 0 then soundB.rvol := temp+1 else soundB.rvol := 0;
  soundB.enabled := soundB.lvol + soundB.rvol <> -2;
  if cr and DSB_TIMER <> 0 then soundB.timer := 1 else soundB.timer := 0;
end;

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

// This function is called *before* any sound related registers are
// modified, to allow the sound simulation to catch up to the current
// time, using the original settings.
procedure soundChanging;
begin
  soundProcess(soundQuotaAtLastFlush - quota + soundCyclesUndone);
  soundQuotaAtLastFlush := quota;
  soundCyclesUndone := 0;
end;

//////////////////////////////////////////////////////////////////////

// vmSetAudioRate() sets the number of CPU cycles that should elapse
// between each generated sound sample.
procedure vmSetAudioRate(cyclesPerSample: integer);
begin
  audioRate := cyclesPerSample;
end;

//////////////////////////////////////////////////////////////////////

// Signal the UI that there are sound samples ready, then clear the
// sample buffer.
procedure soundFlushBuffer;
begin
  OnSoundReady(@soundBuffer, soundBufPos shr 1);
  soundBufPos := 0;
end;

//////////////////////////////////////////////////////////////////////

// Return a pointer to the sample buffer.
procedure vmGetAudioData(var data: pointer; var length: integer);
begin
  data := @soundBuffer;
  length := soundBufPos shr 1;
  soundBufPos := 0;
end;

//////////////////////////////////////////////////////////////////////

// soundProcess() runs the audio simulator for a number of CPU cycles.
procedure soundProcess(cycles: integer);
var
  gbcVolumeMask: integer;
  temp, t2, left, right: integer;
begin
  if (cycles < 0) or (cycles > 1232*228) then Exit;

  // Set the GBC volume step
  gbcVolumeMask := Puint16(@(registers[SOUND_DSOUND_CR]))^ and 3;
  if gbcVolumeMask > 2 then gbcVolumeMask := 2;

  // Add the accumulated sound to the sound buffer
  while cycles > 0 do begin
    Dec(cycles, audioRate);

    left := 0;
    right := 0;

    if registers[SOUND_ENABLED] and (1 shl 7) <> 0 then begin

//////////////////////////////////////////////////////////////////////
// Sound channel 1 ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

      if sound1.enabled then begin
        // Advance the waveform
        Inc(sound1.index, audioRate);
        while sound1.index >= sound1.freq do begin
          Inc(sound1.waveIndex);
          Dec(sound1.index, sound1.freq);
        end;

        // If the sound is timed, advance the timer
        if sound1.timed then begin
          Dec(sound1.soundLength, audioRate);
          sound1.enabled := sound1.soundLength > 0;
        end;

        // Do a possible envelope shift
        if sound1.envRate > 0 then begin
          Inc(sound1.envIndex, audioRate);
          while sound1.envIndex >= sound1.envRate do begin
            if sound1.envIncrease then
              sound1.volume := min(sound1.volume + 1, 15)
            else
              sound1.volume := max(sound1.volume - 1, 0);
            Dec(sound1.envIndex, sound1.envRate);
          end;
        end;

        // Do a possible sweep shift
        if sound1.sweepRate > 0 then begin
          Inc(sound1.sweepIndex, audioRate);
          while sound1.sweepIndex >= sound1.sweepRate do begin
            Dec(sound1.sweepIndex, sound1.sweepRate);

            temp := 2048 - sound1.freq shr 7;

            if sound1.sweepDecrease then begin
              if sound1.sweepStep > 0 then begin
                temp := temp - temp shr sound1.sweepStep;
                if temp >= 0 then sound1.freq := (2048 - temp) * 128;
              end;
            end else begin
              temp := temp + temp shr sound1.sweepStep;
              if temp < 2048 then
                sound1.freq := (2048 - temp) * 128
              else
                sound1.enabled := false;
            end;
          end;
        end;

        // Mix the correct wave state into the left and right composites
        if enableGBC1 then begin
          temp := SquareWaves[sound1.waveDuty and 3, sound1.waveIndex and 7]*sound1.volume;
          if registers[SOUND_ACTIVE] and $01 <> 0 then right := right + temp;
          if registers[SOUND_ACTIVE] and $10 <> 0 then left := left + temp;
        end;
      end;

//////////////////////////////////////////////////////////////////////
// Sound channel 2 ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

      if sound2.enabled then begin
        // Advance the waveform
        Inc(sound2.index, audioRate);
        while sound2.index >= sound2.freq do begin
          Inc(sound2.waveIndex);
          Dec(sound2.index, sound2.freq);
        end;

        // If the sound is timed, advance the timer
        if sound2.timed then begin
          Dec(sound2.soundLength, audioRate);
          sound2.enabled := sound2.soundLength > 0;
        end;

        // Do a possible envelope shift
        if sound2.envRate > 0 then begin
          Inc(sound2.envIndex, audioRate);
          while sound2.envIndex >= sound2.envRate do begin
            if sound2.envIncrease then
              sound2.volume := min(sound2.volume + 1, 15)
            else
              sound2.volume := max(sound2.volume - 1, 15);
            Dec(sound2.envIndex, sound2.envRate);
          end;
        end;

        // Mix the correct wave state into the left and right composites
        if enableGBC2 then begin
          temp := SquareWaves[sound2.waveDuty and 3, sound2.waveIndex and 7] * sound2.volume;
          if registers[SOUND_ACTIVE] and $02 <> 0 then right := right + temp;
          if registers[SOUND_ACTIVE] and $20 <> 0 then left := left + temp;
        end;
      end;

//////////////////////////////////////////////////////////////////////
// Sound channel 3////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

      if sound3.enabled then begin
        // Advance the waveform
        Inc(sound3.index, audioRate);
        while sound3.index >= sound3.freq do begin
          Inc(sound3.waveIndex);
          Dec(sound3.index, sound3.freq);

          if sound3.waveIndex = 32 then begin
            if sound3.doubleLength then sound3swapBanks;
            sound3.waveIndex := 0;
          end;
        end;

        // If the sound is timed, advance the timer
        if sound3.timed then begin
          Dec(sound3.soundLength, audioRate);
          sound3.enabled := sound3.soundLength > 0;
        end;

        // Grab the byte that contains the currently playing nybble
        temp := sound3.backBank[(sound3.waveIndex shr 1) and 15];

        // Grab the correct nybble (opposite to common sense)
        if sound3.waveIndex and 1 <> 0 then temp := temp and $F else temp := temp shr 4;

        // Mix sound 3 into the left and right composites
        if enableGBC3 then begin
//          if temp and 7 <> 0 then temp := temp-16;
//          temp := temp - 8;
//          temp := (temp * sound3.volume) div 4;
          temp := ((temp-8) * sound3.volume) div 4;
          if registers[SOUND_ACTIVE] and $04 <> 0 then right := right + temp;
          if registers[SOUND_ACTIVE] and $40 <> 0 then left := left + temp;
        end;
      end;

//////////////////////////////////////////////////////////////////////
// Sound channel 4 ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

      if sound4.enabled then begin
        // Advance the LFSR
        Inc(sound4.index, audioRate);
        while sound4.index >= sound4.freq do begin
          // Clock a 7/15 stage shift right register with the MSB being fed by an xor tap on bits 0 and 1
          if sound4.use7steps then
            sound4.lfsr := sound4.lfsr shr 1 + (((sound4.lfsr shr 1) and 1) xor (sound4.lfsr and 1)) shl 6
          else
            sound4.lfsr := sound4.lfsr shr 1 + (((sound4.lfsr shr 1) and 1) xor (sound4.lfsr and 1)) shl 14;

          Dec(sound4.index, sound4.freq);
        end;

        // If the sound is timed, advance the timer
        if sound4.timed then begin
          Dec(sound4.soundLength, audioRate);
          sound4.enabled := sound4.soundLength > 0;
        end;

        // Do a possible envelope shift
        if sound4.envRate > 0 then begin
          Inc(sound4.envIndex, audioRate);
          while sound4.envIndex >= sound4.envRate do begin
            if sound4.envIncrease then
              sound4.volume := min(sound4.volume + 1, 15)
            else
              sound4.volume := max(sound4.volume - 1, 0);
            Dec(sound4.envIndex, sound4.envRate);
          end;
        end;

        // The output of channel 4 is the LSB of the shift register
        // modulated by its envelope
        temp := (sound4.lfsr and 1)*sound4.volume;
        if temp = 0 then temp := -sound4.volume;

        if enableGBC4 then begin
          if registers[SOUND_ACTIVE] and $08 <> 0 then right := right + temp;
          if registers[SOUND_ACTIVE] and $80 <> 0 then left := left + temp;
        end;
      end;

//////////////////////////////////////////////////////////////////////
// Sound channels 1 to 4 rescaling ///////////////////////////////////
//////////////////////////////////////////////////////////////////////

      // Convert the 4 bit output from above to a 9 bit value
      temp := registers[SOUND_VOLUME];
      left := (left*((temp shr 4) and $7)) * (1 shl gbcVolumeMask);
      right := (right*(temp and $7)) * (1 shl gbcVolumeMask);

//////////////////////////////////////////////////////////////////////
// Sound channel A ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

      temp := 0;
      t2 := 0;
      if soundA.enabled and enableDSA then begin
        temp := temp + soundA.latched * soundA.lvol;
        t2 := t2 + soundA.latched * soundA.rvol;
      end;

//////////////////////////////////////////////////////////////////////
// Sound channel B ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

      if soundB.enabled and enableDSB then begin
        temp := temp + soundB.latched * soundB.lvol;
        t2 := t2 + soundB.latched * soundB.rvol;
      end;

      left := left + temp;
      right := right + t2;
    end;

//////////////////////////////////////////////////////////////////////

    // Stick the sample in the buffer
    soundBuffer[soundBufPos] := max(min(left div 2, 255), 0);
    soundBuffer[soundBufPos + 1] := max(min(right div 2, 255), 0);
    soundBufPos := (soundBufPos + 2) and BUFFER_MASK;
  end;
end;

//////////////////////////////////////////////////////////////////////
// DS FIFO management ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

// Read a sample out of a DS FIFO
procedure DequeueTDSoundRecord(var r: TDSoundRecord);
var
  i: integer;
begin
  if r.writeIndex > 0 then begin
    r.latched := r.fifo[0];
    for i := 1 to 31 do r.fifo[i-1] := r.fifo[i];
    Dec(r.writeIndex, 1);
  end;
end;

//////////////////////////////////////////////////////////////////////

// Add a sample to a DS FIFO
procedure QueueTDSoundRecord(var r: TDSoundRecord; data: int8);
begin
  if r.writeIndex < 32 then begin
    r.fifo[r.writeIndex] := data;
    r.writeIndex := r.writeIndex + 1;
  end;
end;

//////////////////////////////////////////////////////////////////////

// Empty a DS FIFO
procedure ResetTDSoundRecord(var r: TDSoundRecord);
begin
  r.writeIndex := 0;
  FillChar(r.fifo, 32, 0);
end;

//////////////////////////////////////////////////////////////////////

end.

//////////////////////////////////////////////////////////////////////

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -