📄 cpusound.pas
字号:
//////////////////////////////////////////////////////////////////////
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 + -