Ricoh RF5C164 PCM sound chip
Divider of sub CPU cycles
11const RF5C164_DIVIDER: u64 = 384;
13const ADDRESS_FRACT_BITS: u32 = 11; 14const ADDRESS_FRACT_MASK: u32 = (1 << ADDRESS_FRACT_BITS) - 1; 15 16const WAVEFORM_RAM_LEN: usize = 64 * 1024; 17const WAVEFORM_ADDRESS_MASK: u32 = WAVEFORM_RAM_LEN as u32 - 1; 18 19const ADDRESS_FIXED_POINT_MASK: u32 = (1 << (16 + ADDRESS_FRACT_BITS)) - 1; 20 21type WaveformRam = [u8; WAVEFORM_RAM_LEN]; 22 23#[derive(Debug, Clone, Default, Encode, Decode)] 24struct InterpolationBuffer { 25 buffer: [i8; 6], 26} 27 28impl InterpolationBuffer { 29 fn clear(&mut self) { 30 self.buffer.fill(0); 31 } 32 33 fn push(&mut self, sample: i8) { 34 for i in 0..5 { 35 self.buffer[i] = self.buffer[i + 1]; 36 } 37 self.buffer[5] = sample; 38 } 39 40 fn sample(&self, interpolation: PcmInterpolation, current_address: u32) -> f64 { 41 match interpolation { 42 PcmInterpolation::None => self.buffer[5].into(), 43 PcmInterpolation::Linear => { 44 interpolate_linear(self.buffer[4], self.buffer[5], interpolation_x(current_address)) 45 } 46 PcmInterpolation::CubicHermite => interpolate_cubic_4p( 47 self.buffer[2..6].try_into().unwrap(), 48 interpolation_x(current_address), 49 ), 50 PcmInterpolation::CubicHermite6Point => { 51 interpolate_cubic_6p(self.buffer, interpolation_x(current_address)) 52 } 53 } 54 } 55} 56 57fn interpolation_x(address: u32) -> f64 { 58 f64::from(address & ADDRESS_FRACT_MASK) / f64::from(1 << ADDRESS_FRACT_BITS) 59} 60 61fn interpolate_linear(y0: i8, y1: i8, x: f64) -> f64 { 62 let y0: f64 = y0.into(); 63 let y1: f64 = y1.into(); 64 65 y0 * (1.0 - x) + y1 * x 66}
Clamp to [-127, 126] because samples are sign+magnitude, not signed 8-bit +127 is not a valid sample value because 0xFF is the loop end marker
73fn interpolate_cubic_4p(samples: [i8; 4], x: f64) -> f64 { 74 let result = jgenesis_common::audio::interpolate_cubic_hermite_4p(samples.map(f64::from), x); 75 result.clamp(MIN_SAMPLE, MAX_SAMPLE) 76} 77 78fn interpolate_cubic_6p(samples: [i8; 6], x: f64) -> f64 { 79 let result = jgenesis_common::audio::interpolate_cubic_hermite_6p(samples.map(f64::from), x); 80 result.clamp(MIN_SAMPLE, MAX_SAMPLE) 81} 82 83#[derive(Debug, Clone, Default, Encode, Decode)] 84struct Channel { 85 enabled: bool, 86 start_address: u16, 87 loop_address: u16, 88 master_volume: u8, 89 l_volume: u8, 90 r_volume: u8, 91 // Fixed point 16.11 92 current_address: u32, 93 // Fixed point 5.11 94 address_increment: u16, 95 interpolation_buffer: InterpolationBuffer, 96} 97 98impl Channel { 99 fn enable(&mut self, waveform_ram: &WaveformRam) { 100 if !self.enabled { 101 self.current_address = u32::from(self.start_address) << ADDRESS_FRACT_BITS; 102 self.interpolation_buffer.clear(); 103 self.enabled = true; 104 105 // Immediately read the first sample when a channel is enabled; otherwise it will get skipped 106 let first_sample = waveform_ram[self.start_address as usize]; 107 if first_sample != 0xFF { 108 self.interpolation_buffer.push(sign_magnitude_to_pcm(first_sample)); 109 } 110 } 111 } 112 113 fn disable(&mut self) { 114 self.enabled = false; 115 } 116 117 fn clock(&mut self, waveform_ram: &WaveformRam) { 118 if !self.enabled { 119 return; 120 } 121 122 let address_increment: u32 = self.address_increment.into(); 123 let incremented_address = self.current_address + address_increment; 124 125 let mut address = self.current_address >> ADDRESS_FRACT_BITS; 126 let steps = (incremented_address >> ADDRESS_FRACT_BITS) - address; 127 if steps == 0 { 128 // Only the fractional bits changed; no new samples read 129 self.current_address = incremented_address & ADDRESS_FIXED_POINT_MASK; 130 return; 131 } 132 133 // All steps but last 134 for _ in 0..steps - 1 { 135 address = (address + 1) & WAVEFORM_ADDRESS_MASK; 136 let sample = waveform_ram[address as usize]; 137 if sample == 0xFF { 138 // Loop signal 139 // Actual hardware would skip over this, so just ignore it. 140 // This shouldn't really happen in practice unless a game puts multiple loop markers 141 // at the end of a sample while playing at >32552 Hz to guarantee that the chip 142 // doesn't miss the loop. 143 continue; 144 } 145 146 self.interpolation_buffer.push(sign_magnitude_to_pcm(sample)); 147 } 148 149 // Last step 150 address = (address + 1) & WAVEFORM_ADDRESS_MASK; 151 let sample = waveform_ram[address as usize]; 152 if sample == 0xFF { 153 // Loop signal; jump to start of loop and immediately read the next sample 154 address = self.loop_address.into(); 155 let loop_start_sample = waveform_ram[self.loop_address as usize]; 156 if loop_start_sample == 0xFF { 157 // Infinite loop 158 // TODO what does actual hardware do when there's an infinite loop? 159 self.interpolation_buffer.push(0); 160 } else { 161 self.interpolation_buffer.push(sign_magnitude_to_pcm(loop_start_sample)); 162 } 163 } else { 164 self.interpolation_buffer.push(sign_magnitude_to_pcm(sample)); 165 } 166 167 let new_address_int = address & WAVEFORM_ADDRESS_MASK; 168 let new_address_fract = incremented_address & ADDRESS_FRACT_MASK; 169 self.current_address = (new_address_int << ADDRESS_FRACT_BITS) | new_address_fract; 170 } 171 172 fn sample(&self, interpolation: PcmInterpolation) -> (i32, i32) { 173 if !self.enabled { 174 return (0, 0); 175 } 176 177 let sample = self.interpolation_buffer.sample(interpolation, self.current_address); 178 let sign = sample.signum() as i32; 179 let magnitude = sample.abs(); 180 181 // Apply volume 182 let amplified = magnitude * f64::from(self.master_volume); 183 let panned_l = amplified * f64::from(self.l_volume); 184 let panned_r = amplified * f64::from(self.r_volume); 185 186 // Drop the lowest 5 bits and apply sign 187 // Per the RF5C164 datasheet, the truncation is done purely on the magnitude, before taking 188 // sign into account 189 let output_l = sign * ((panned_l.round() as i32) >> 5); 190 let output_r = sign * ((panned_r.round() as i32) >> 5); 191 192 (output_l, output_r) 193 } 194} 195 196fn sign_magnitude_to_pcm(sample: u8) -> i8 { 197 // RF5C164 samples have a sign bit and a 7-bit magnitude 198 // Sign bit 1 = Positive, 0 = Negative 199 let magnitude = (sample & 0x7F) as i8; 200 if sample.bit(7) { magnitude } else { -magnitude } 201} 202 203#[derive(Debug, Clone, Encode, Decode)] 204pub struct Rf5c164 { 205 enabled: bool, 206 channels: [Channel; 8], 207 waveform_ram: BoxedByteArray<WAVEFORM_RAM_LEN>, 208 waveform_ram_bank: u8, 209 selected_channel: u8, 210 divider: u64, 211 interpolation: PcmInterpolation, 212} 213 214impl Rf5c164 { 215 pub fn new(config: &SegaCdEmulatorConfig) -> Self { 216 Self { 217 enabled: false, 218 channels: array::from_fn(|_| Channel::default()), 219 waveform_ram: BoxedByteArray::new(), 220 waveform_ram_bank: 0, 221 selected_channel: 0, 222 divider: RF5C164_DIVIDER, 223 interpolation: config.pcm_interpolation, 224 } 225 } 226 227 pub fn read(&self, address: u32) -> u8 { 228 match address { 229 0x0000..=0x0007 | 0x0009..=0x000F | 0x0020..=0x0FFF => { 230 // Unused for reads 231 0x00 232 } 233 0x0008 => self.read_channel_on_register(), 234 0x0010..=0x001F => self.read_channel_address(address), 235 0x1000..=0x1FFF => { 236 // Reading waveform RAM is only allowed while the chip is not running 237 if !self.enabled { 238 let waveform_ram_addr = 239 (u32::from(self.waveform_ram_bank) << 12) | (address & 0x0FFF); 240 self.waveform_ram[waveform_ram_addr as usize] 241 } else { 242 0x00 243 } 244 } 245 _ => panic!("invalid RF5C164 address: {address:06X}"), 246 } 247 } 248 249 pub fn write(&mut self, address: u32, value: u8) { 250 match address { 251 0x0000..=0x0008 => { 252 self.write_register(address, value); 253 } 254 0x0009..=0x0FFF => { 255 // Unused 256 } 257 0x1000..=0x1FFF => { 258 let waveform_ram_addr = 259 (u32::from(self.waveform_ram_bank) << 12) | (address & 0x0FFF); 260 self.waveform_ram[waveform_ram_addr as usize] = value; 261 } 262 _ => panic!("invalid RF5C164 address: {address:06X}"), 263 } 264 } 265 266 pub fn dma_write(&mut self, address: u32, value: u8) { 267 let waveform_ram_addr = (u32::from(self.waveform_ram_bank) << 12) | address; 268 self.waveform_ram[waveform_ram_addr as usize] = value; 269 } 270 271 pub fn disable(&mut self) { 272 self.enabled = false; 273 } 274 275 fn read_channel_on_register(&self) -> u8 { 276 log::trace!("Channel on/off register read"); 277 278 self.channels 279 .iter() 280 .enumerate() 281 .map(|(i, channel)| u8::from(channel.enabled) << i) 282 .reduce(|a, b| a | b) 283 .unwrap() 284 } 285 286 fn read_channel_address(&self, address: u32) -> u8 { 287 let channel_idx = (address & 0xF) >> 1; 288 let channel = &self.channels[channel_idx as usize]; 289 let channel_address = if channel.enabled { 290 (channel.current_address >> ADDRESS_FRACT_BITS) as u16 291 } else { 292 channel.start_address 293 }; 294 295 log::trace!("Channel {channel_idx} address read; current address = {channel_address:04X}"); 296 297 if address.bit(0) { 298 // High byte 299 channel_address.msb() 300 } else { 301 // Low byte 302 channel_address.lsb() 303 } 304 } 305 306 fn write_register(&mut self, address: u32, value: u8) { 307 log::trace!( 308 "PCM register: Wrote {value:02X} to {address:04X}, current channel is {}", 309 self.selected_channel 310 ); 311 312 match address { 313 0x0000 => { 314 // Envelope 315 self.channels[self.selected_channel as usize].master_volume = value; 316 317 log::trace!(" Master volume = {value:02X}"); 318 } 319 0x0001 => { 320 // Pan 321 let channel = &mut self.channels[self.selected_channel as usize]; 322 channel.l_volume = value & 0x0F; 323 channel.r_volume = value >> 4; 324 325 log::trace!( 326 " L volume = {:02X}, R volume = {:02X}", 327 channel.l_volume, 328 channel.r_volume 329 ); 330 } 331 0x0002 => { 332 // Address increment, low byte 333 let channel = &mut self.channels[self.selected_channel as usize]; 334 channel.address_increment.set_lsb(value); 335 336 log::trace!(" Address increment = {:04X}", channel.address_increment); 337 } 338 0x0003 => { 339 // Address increment, high byte 340 let channel = &mut self.channels[self.selected_channel as usize]; 341 channel.address_increment.set_msb(value); 342 343 log::trace!(" Address increment = {:04X}", channel.address_increment); 344 } 345 0x0004 => { 346 // Loop address, low byte 347 let channel = &mut self.channels[self.selected_channel as usize]; 348 channel.loop_address.set_lsb(value); 349 350 log::trace!(" Loop address = {:04X}", channel.loop_address); 351 } 352 0x0005 => { 353 // Loop address, high byte 354 let channel = &mut self.channels[self.selected_channel as usize]; 355 channel.loop_address.set_msb(value); 356 357 log::trace!(" Loop address = {:04X}", channel.loop_address); 358 } 359 0x0006 => { 360 // Start address (low byte is always $00) 361 self.channels[self.selected_channel as usize].start_address = 362 u16::from_be_bytes([value, 0x00]); 363 364 log::trace!(" Start address = {value:02X}00"); 365 } 366 0x0007 => { 367 // Control register 368 self.enabled = value.bit(7); 369 370 log::trace!("Chip enabled = {}", self.enabled); 371 372 // Bits 3-0 have different effects depending on the value of bit 6 373 if value.bit(6) { 374 // Change selected channel (3 bits) 375 self.selected_channel = value & 0x07; 376 377 log::trace!(" Selected channel = {}", self.selected_channel); 378 } else { 379 // Change waveform RAM bank (4 bits) 380 self.waveform_ram_bank = value & 0x0F; 381 382 log::trace!(" PCM waveform RAM bank = {:X}", self.waveform_ram_bank); 383 } 384 } 385 0x0008 => { 386 // Channel on/off register 387 // 1 = Disabled, 0 = Enabled 388 for (i, channel) in self.channels.iter_mut().enumerate() { 389 if value.bit(i as u8) { 390 channel.disable(); 391 } else { 392 channel.enable(&self.waveform_ram); 393 } 394 } 395 } 396 _ => panic!("invalid RF5C164 register address: {address:06X}"), 397 } 398 } 399 400 pub fn tick(&mut self, mut sub_cpu_cycles: u64, mut audio_callback: impl FnMut((f64, f64))) { 401 while sub_cpu_cycles >= self.divider { 402 sub_cpu_cycles -= self.divider; 403 self.divider = RF5C164_DIVIDER; 404 405 if self.enabled { 406 self.clock(); 407 } 408 409 audio_callback(self.sample()); 410 } 411 self.divider -= sub_cpu_cycles; 412 } 413 414 fn clock(&mut self) { 415 for channel in &mut self.channels { 416 channel.clock(&self.waveform_ram); 417 } 418 } 419 420 pub fn sample(&self) -> (f64, f64) { 421 if !self.enabled { 422 return (0.0, 0.0); 423 } 424 425 let (sample_l, sample_r) = self 426 .channels 427 .iter() 428 .map(|channel| channel.sample(self.interpolation)) 429 .fold((0, 0), |(sum_l, sum_r), (sample_l, sample_r)| { 430 (sum_l + sample_l, sum_r + sample_r) 431 }); 432 433 // Individual channel samples are effectively signed 15-bit after applying volume (and 434 // dropping the lowest 5 bits) 435 // Mixed output is clamped to signed 16-bit 436 let sample_l = sample_l.clamp(i16::MIN.into(), i16::MAX.into()); 437 let sample_r = sample_r.clamp(i16::MIN.into(), i16::MAX.into()); 438 439 let sample_l = f64::from(sample_l) / -f64::from(i16::MIN); 440 let sample_r = f64::from(sample_r) / -f64::from(i16::MIN); 441 (sample_l, sample_r) 442 } 443 444 pub fn reload_config(&mut self, config: &SegaCdEmulatorConfig) { 445 self.interpolation = config.pcm_interpolation; 446 } 447 448 pub fn debug_ram_view(&mut self) -> impl DebugMemoryView { 449 DebugBytesView(self.waveform_ram.as_mut_slice()) 450 } 451}