#### YunoSynth
#### Copyright (C) 2023 Remilia Scarlet <remilia@posteo.jp>
#### Portions based on VGMPlay, Copyright (C) Valley Bell
####
#### This program is free software: you can redistribute it and/or
#### modify it under the terms of the GNU Affero General Public
#### License as published by the Free Software Foundation, either
#### version 3 of the License, or (at your option) any later version.
####
#### This program is distributed in the hope that it will be useful,
#### but WITHOUT ANY WARRANTY; without even the implied warranty of
#### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#### Affero General Public License for more details.
####
#### You should have received a copy of the GNU Affero General Public License
#### along with this program. If not, see <https://www.gnu.org/licenses/>.
require "./emu-c140-mame"
####
#### Namco C140 sound chip emulator interface
####
module Yuno::Chips
# C140 sound chip emulator.
class C140 < Yuno::AbstractChip
CHIP_ID = 0x1C_u32
@clockFromHeader : UInt32 = 0
@samplingMode : UInt8 = 0
@chipSampleRate : UInt32 = 0
@chip : C140Mame? = nil
@subtype : UInt8 = 0
def initialize(chipNum : Int32, absChipCount : Int32, vgm : VgmFile, playbackSampleRate : UInt32,
newSamplingMode : UInt8, newPlayerSampleRate : UInt32, *, emuCore : Symbol? = nil,
flags : ChipFlags? = nil)
@clockFromHeader = vgm.getAlternateChipClock(chipNum, Yuno::ChipType::C140) || vgm.header.c140Clock
@clockFromHeader &= 0x3FFFFFFF
@playerSampleRate = newPlayerSampleRate
@subtype = vgm.header.c140ChipType
chipCount = (vgm.header.c140Clock & 0x40000000) != 0 ? 2 : 1
initFields(vgm, emuCore, playbackSampleRate, newSamplingMode, chipCount, flags)
end
protected def initialize
end
protected def initFields(vgm : VgmFile, emuCore : Symbol?, playbackSampleRate : UInt32, newSamplingMode : UInt8,
chipCount : Int32, flags : ChipFlags?) : Nil
@volume = vgm.getChipVolume(self, ChipType::C140, chipCount)
@core = emuCore || C140.defaultEmuCore
case @core
when :mame
@samplingMode = newSamplingMode
@chipSampleRate = @clockFromHeader <= 0 ? playbackSampleRate : @clockFromHeader
else raise YunoError.new("Unsupported emulation core for C140: #{@core}")
end
end
def type : ChipType
ChipType::C140
end
def name : String
case @subtype
when 0, 1 then "Namco C140"
when 2 then "Namco C219 ASIC"
else "Namco C140 derivative"
end
end
def shortName : String
case @subtype
when 0, 1 then "C140"
when 2 then "C219"
else "C140"
end
end
def id : UInt32
CHIP_ID
end
def emuCore : Symbol
@core
end
def self.defaultEmuCore : Symbol
:mame
end
def start(chipIndex : UInt8, clock : UInt32, flags : ChipFlags? = nil) : UInt32
case @core
when :mame
rate = if clock < 1000000
clock
else
clock.tdiv(384)
end
srate = if (Yuno.bitflag?(@samplingMode, 0x01) && rate < @chipSampleRate) || @samplingMode == 0x02
@chipSampleRate
else
rate
end
raise "Cannot start C140" if rate >= 0x1000000 # Limit to 16 MHz sample rate (32 MB buffer)
@chip = C140Mame.new(rate, srate, C140Mame::BankingType.from_value(@subtype))
@sampleRate = @chip.not_nil!.rate # Update sample rate
@sampleRate
else raise YunoError.new("Unsupported emulation core for C140: #{@core}")
end
end
def getClock : UInt32
@clockFromHeader
end
def getStartFlags(vgm : VgmFile) : ChipFlags?
nil
end
def update(chipIndex : UInt8, outputs : OutputBuffers, samples : Int) : Nil
@chip.not_nil!.update(outputs, samples.to_u32!)
end
def reset(chipIndex : UInt8) : Nil
@chip.not_nil!.reset
end
def read(chipIndex : UInt8, offset : Int) : UInt8|UInt16|UInt32
@chip.not_nil!.read(offset)
end
def write(chipIndex : UInt8, offset : Int, data : Int, port : UInt8 = 0) : Nil
@chip.not_nil!.write(offset.to_i32!, data.to_u8!)
end
def writeDac(port : Int, command : Int, data : Int) : Nil
write(0, (port.to_i32! << 8) | command, data)
end
def setMuteMask(chipIndex : UInt8, mask : UInt32) : Nil
@chip.not_nil!.muteMask = mask
end
def getVolModifier : UInt32
@volume.to_u32
end
def baseVolume : UInt16
0x100_u16
end
@[AlwaysInline]
def writeRom(romSize : Int32, dataStart : Int32, dataLength : Int32, romData : Slice(UInt8)) : Nil
@chip.not_nil!.writeRom(romSize, dataStart, dataLength, romData)
end
# The internal interface for the chip.
@[AlwaysInline]
def chip : AbstractEmulator
@chip.not_nil!
end
end
end