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

📄 cpusound.pas

📁 一个不出名的GBA模拟器
💻 PAS
📖 第 1 页 / 共 2 页
字号:
//////////////////////////////////////////////////////////////////////
//                                                                  //
// cpuSound.pas: Sound simulation code                              //
//   This manages sound channels 1,2,3,4,A,B                        //
//                                                                  //
// The contents of this file are subject to the Bottled Light       //
// Public License Version 1.0 (the "License"); you may not use this //
// file except in compliance with the License. You may obtain a     //
// copy of the License at http://www.bottledlight.com/BLPL/         //
//                                                                  //
// Software distributed under the License is distributed on an      //
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or   //
// implied. See the License for the specific language governing     //
// rights and limitations under the License.                        //
//                                                                  //
// The Original Code is the Mappy VM Core, released April 1st, 2003 //
// The Initial Developer of the Original Code is Bottled Light,     //
// Inc. Portions created by Bottled Light, Inc. are Copyright       //
// (C) 2001 - 2003 Bottled Light, Inc. All Rights Reserved.         //
//                                                                  //
// Author(s):                                                       //
//   Michael Noland (joat), michael@bottledlight.com                //
//                                                                  //
// Changelog:                                                       //
//   1.0: First public release (April 1st, 2003)                    //
//                                                                  //
// Notes:                                                           //
//   Channel 1 is a square wave with envelope and sweep control     //
//   Channel 2 is a square wave with only envelope control          //
//   Channel 3 is a 4 bit arbitrary waveform with envelope control  //
//   Channel 4 is a LFSR 'pseudonoise' channel /w envelope control  //
//   Channels A and B are 8 bit FIFO fed waveforms                  //
//                                                                  //
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
unit cpuSound; ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

uses
  SysUtils, Math, nexus, AddressSpace;

//////////////////////////////////////////////////////////////////////
// Overall control functions /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

// soundProcess() runs the audio simulator for a number of CPU cycles.
procedure soundProcess(cycles: integer);

// Signal the UI that there are sound samples ready, then clear the
// sample buffer.
procedure soundFlushBuffer;

// 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;

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

// vmGetAudioData() is called to get a pointer to the sample buffer,
// which is stored in the data pointer.  The number of valid samples
// in that buffer is stuffed in length.  The buffer size is actually
// length * 2 bytes, since each sample consists of both a left and a
// right volume level (even bytes = L, odd bytes = R).
procedure vmGetAudioData(var data: pointer; var length: integer);

//////////////////////////////////////////////////////////////////////
// These functions are called from cpuMemory whenever the
// corresponding registers are modified
//////////////////////////////////////////////////////////////////////

procedure soundSetSound1Sweep;
procedure soundSetSound1Length;
procedure soundSetSound1Freq;

procedure soundSetSound2Length;
procedure soundSetSound2Freq;

procedure soundSetSound3CR;
procedure soundSetSound3Length;
procedure soundSetSound3Freq;

procedure soundSetSound4Length;
procedure soundSetSound4CR;

procedure soundIncrementFifoA;
procedure soundIncrementFifoB;

procedure soundWriteDSoundCR;

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

procedure QueueTDSoundRecord(var r: TDSoundRecord; data: int8);
procedure ResetTDSoundRecord(var r: TDSoundRecord);
procedure DequeueTDSoundRecord(var r: TDSoundRecord);

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

exports
  vmSetAudioRate,
  vmGetAudioData;

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

var
  // Flags that enable sound generation on the different audio
  // channels, provided so the UI can filter channels.
  enableGBC1, enableGBC2, enableGBC3, enableGBC4: boolean;
  enableDSA, enableDSB: boolean;

//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

uses
  cpuMemory, cpuMisc;

//////////////////////////////////////////////////////////////////////
// Globals shared between the sound functions ////////////////////////
//////////////////////////////////////////////////////////////////////

const
  BUFFER_MASK = 32767;

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

var
  // even bytes = L, odd bytes = R
  soundBuffer: array[0..BUFFER_MASK] of uint8;
  soundBufPos: integer;
  audioRate: integer;

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

const
  // Channel 1 and 2 waveforms at different duty cycles
  SquareWaves: array[0..3, 0..7] of integer =
    ((+1,-1,-1,-1,-1,-1,-1,-1),
     (+1,+1,-1,-1,-1,-1,-1,-1),
     (+1,+1,+1,+1,-1,-1,-1,-1),
     (+1,+1,+1,+1,+1,+1,-1,-1));
//    ((-1,-1,1,-1,-1,-1,-1,-1),
//     (-1,1,1,-1,-1,-1,-1,-1),
//     (-1,1,1,1,1,-1,-1,-1),
//     (1,-1,-1,1,1,1,1,1));

//////////////////////////////////////////////////////////////////////
// These functions are called from cpuMemory whenever the           //
// corresponding registers are modified                             //
//////////////////////////////////////////////////////////////////////

procedure soundIncrementFifoA;
begin
  QueueTDSoundRecord(soundA, registers[SOUNDA_FIFO+0]);
  QueueTDSoundRecord(soundA, registers[SOUNDA_FIFO+1]);
  QueueTDSoundRecord(soundA, registers[SOUNDA_FIFO+2]);
  QueueTDSoundRecord(soundA, registers[SOUNDA_FIFO+3]);
end;

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

procedure soundIncrementFifoB;
begin
  QueueTDSoundRecord(soundB, registers[SOUNDB_FIFO+0]);
  QueueTDSoundRecord(soundB, registers[SOUNDB_FIFO+1]);
  QueueTDSoundRecord(soundB, registers[SOUNDB_FIFO+2]);
  QueueTDSoundRecord(soundB, registers[SOUNDB_FIFO+3]);
end;

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

procedure soundSetSound1Sweep;
var
  r: byte;
begin
  r := registers[SOUND1_SWEEP];

  // the interval between sweeps is length/128 seconds, or length*131072 cycles
  sound1.sweepRate := ((r shr 4) and 7) * 131072;
  sound1.sweepDecrease := r and SOUND_SWEEP_DECREASE <> 0;
  sound1.sweepStep := r and 7;

  // Turn off the sound if theres a non-zero sweep rate when sweeping is disabled
  if (sound1.sweepRate = 0) and (sound1.sweepStep > 0) and not sound1.sweepDecrease then sound1.enabled := false;
end;

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

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

  // total sound length is (64-length)/256 seconds, or (64-length)*65536 cycles
  sound1.soundLength := (64 - r and 63)*65536;
  sound1.waveDuty := (r shr 6) and 3;

  if not sound1.enabled then begin
    sound1.envIncrease := (r shr 8) and SOUND_ENVELOPE_INCREASE <> 0;
    sound1.volume := r shr 12;

    // envelope step rate is length/64 secs, or length*262144 cycles
    sound1.envRate := ((r shr 8) and 3)*262144;
  end;
end;

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

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

  // If this bit is written, reset sound 1
  if r and SOUND_RESTART <> 0 then begin
    sound1.enabled := false;
    soundSetSound1Sweep;
    soundSetSound1Length;
    sound1.index := 0;
//    sound1.waveIndex := 0; according to belogic, no reset of this
    sound1.envIndex := 0;
    sound1.sweepIndex := 0;
    sound1.enabled := true;
  end;

  sound1.timed := r and (1 shl 14) <> 0;

  // the sound frequency is 131072/(2048-freq) Hz, every (2048-freq)*128 cycles
  sound1.freq := (2048 - r and $7FF) * 128;

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

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

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

  // total sound length is (64-length)/256 seconds, or (64-length)*65536 cycles
  sound2.soundLength := (64 - r and 63)*65536;
  sound2.waveDuty := (r shr 6) and 3;

  if not sound2.enabled then begin
    sound2.envIncrease := (r shr 8) and SOUND_ENVELOPE_INCREASE <> 0;
    sound2.volume := r shr 12;

    // envelope step rate is length/64 secs, or length*262144 cycles
    sound2.envRate := ((r shr 8) and 3)*262144;
  end;
end;

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

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

  // If this bit is written, reset sound 2
  if r and SOUND_RESTART <> 0 then begin
    sound2.enabled := false;
    soundSetSound2Length;
    sound2.index := 0;
    sound2.waveIndex := 0;
    sound2.envIndex := 0;
    sound2.enabled := true;
  end;

  sound2.timed := r and (1 shl 14) <> 0;

  // the sound frequency is 131072/(2048-freq) Hz, every (2048-freq)*128 cycles
  sound2.freq := (2048 - r and $7FF) * 128;

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

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

procedure sound3swapBanks;
var
  temp: byte;
  i: integer;
begin
  for i := 0 to 15 do begin
    temp := sound3.backBank[i];
    sound3.backBank[i] := registers[SOUND3_PATTERN+i];
    registers[SOUND3_PATTERN+i] := temp;
  end;
  sound3.curBank := not sound3.curBank;
end;

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

procedure soundSetSound3CR;
var
  r: byte;
  bank: boolean;
begin
  r := registers[SOUND3_CR];
//  sound3.enabled := sound3.enabled or (r and SOUND3_ACTIVE <> 0);
  sound3.doubleLength := r and SOUND3_64SAMPLES <> 0;
  bank := r and SOUND3_BANK1 <> 0;
  if bank <> sound3.curBank then sound3swapBanks;
end;

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

procedure soundSetSound3Length;
// JSensebe says: 000=mute, 001=full vol, 010=1/2 vol, 011=1/4 vol, 1xx=3/4 vol
const
  sound3volumes: array[0..7] of integer = (0,4,2,1,3,3,3,3);
var
  r: uint16;
begin
  r := Puint16(@(registers[SOUND3_LENGTH]))^;

  // total sound length is (256-length)/256 seconds, or (256-length)*65536 cycles
  sound3.soundLength := (256 - r and $FF)*65536;
  sound3.volume := sound3volumes[r shr 13];
end;

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

procedure soundSetSound3Freq;
var
  r: uint16;
  i: integer;
begin
  r := Puint16(@(registers[SOUND3_FREQUENCY]))^;

  // If this bit is written, reset sound 3
  if r and SOUND_RESTART <> 0 then begin
    sound3.enabled := false;
    soundSetSound3Length;
    sound3.index := 0;
    sound3.waveIndex := 0;
    sound3.enabled := registers[SOUND3_CR] and SOUND3_ACTIVE <> 0;

    for i := 0 to 15 do begin
      sound3.backBank[i] := 0;
      registers[SOUND3_PATTERN+i] := 0;
    end;
  end;

  sound3.timed := r and (1 shl 14) <> 0;

  // the sound frequency is 131072/(2048-freq) Hz, every (2048-freq)*128 cycles
  sound3.freq := (2048 - r and $7FF) * 128;

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

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

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

  // total sound length is (64-length)/256 seconds, or (64-length)*65536 cycles
  sound4.soundLength := (64 - r and 63)*65536;

  if not sound4.enabled then begin
    sound4.envIncrease := (r shr 8) and SOUND_ENVELOPE_INCREASE <> 0;
    sound4.volume := r shr 12;

    // envelope rate is length/64 secs, or length*262144 cycles
    sound4.envRate := ((r shr 8) and 3)*262144;
  end;
end;

⌨️ 快捷键说明

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