mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-27 03:31:31 +00:00
Some progress on sound I guess
This commit is contained in:
parent
10848d28eb
commit
060dbde966
@ -1,15 +1,13 @@
|
|||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use crate::utils::{
|
use crate::utils::join_bytes;
|
||||||
join_bytes
|
|
||||||
};
|
|
||||||
use crate::rom::{ROM, load_rom};
|
use crate::rom::{ROM, load_rom};
|
||||||
use crate::ppu::{
|
use crate::ppu::{
|
||||||
PPU,
|
PPU,
|
||||||
DMA_ADDRESS,
|
DMA_ADDRESS,
|
||||||
};
|
};
|
||||||
use crate::timer::{Timer};
|
use crate::timer::Timer;
|
||||||
use crate::joypad::{Joypad, JOYPAD_ADDRESS};
|
use crate::joypad::{Joypad, JOYPAD_ADDRESS};
|
||||||
use crate::sound::{Sound};
|
use crate::sound::Sound;
|
||||||
use crate::interrupts::{
|
use crate::interrupts::{
|
||||||
Interrupts,
|
Interrupts,
|
||||||
INTERRUPT_ENABLE_ADDRESS,
|
INTERRUPT_ENABLE_ADDRESS,
|
||||||
|
@ -8,7 +8,7 @@ use crate::utils::{
|
|||||||
sub_half_carry,
|
sub_half_carry,
|
||||||
add_half_carry_16bit,
|
add_half_carry_16bit,
|
||||||
};
|
};
|
||||||
use crate::bus::{Bus};
|
use crate::bus::Bus;
|
||||||
use crate::interrupts::{
|
use crate::interrupts::{
|
||||||
Interrupt,
|
Interrupt,
|
||||||
INTERRUPT_ENABLE_ADDRESS,
|
INTERRUPT_ENABLE_ADDRESS,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// use std::{thread, time};
|
// use std::{thread, time};
|
||||||
use winit_input_helper::WinitInputHelper;
|
use winit_input_helper::WinitInputHelper;
|
||||||
use winit::event::{VirtualKeyCode};
|
use winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
use crate::cpu::{CPU, Cycles};
|
use crate::cpu::{CPU, Cycles};
|
||||||
use crate::interrupts::Interrupt;
|
use crate::interrupts::Interrupt;
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
use crate::joypad::{Button};
|
use crate::joypad::Button;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use crate::rom::{save_file};
|
use crate::rom::{save_file};
|
||||||
|
|
||||||
@ -111,6 +111,7 @@ impl Emulator {
|
|||||||
let cycles = self.cpu.get_last_op_cycles().to_t();
|
let cycles = self.cpu.get_last_op_cycles().to_t();
|
||||||
self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, frame_buffer);
|
self.bus.ppu.do_cycles(&mut self.bus.interrupts, cycles, frame_buffer);
|
||||||
self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
|
self.bus.timer.do_cycles(&mut self.bus.interrupts, cycles);
|
||||||
|
self.bus.sound.do_cycles(cycles);
|
||||||
|
|
||||||
// 1 CPU cycle = 238.42ns
|
// 1 CPU cycle = 238.42ns
|
||||||
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
|
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
|
||||||
|
@ -3,8 +3,8 @@ use crate::utils::{
|
|||||||
get_bit,
|
get_bit,
|
||||||
set_bit,
|
set_bit,
|
||||||
};
|
};
|
||||||
use crate::bus::{SPRITE_ATTRIBUTE_TABLE};
|
use crate::bus::SPRITE_ATTRIBUTE_TABLE;
|
||||||
use crate::cpu::{Cycles};
|
use crate::cpu::Cycles;
|
||||||
use crate::interrupts::{Interrupts, Interrupt};
|
use crate::interrupts::{Interrupts, Interrupt};
|
||||||
|
|
||||||
pub const LCD_WIDTH: u32 = 160;
|
pub const LCD_WIDTH: u32 = 160;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::emulator::Emulator;
|
use crate::emulator::Emulator;
|
||||||
use crate::frames::Frames;
|
use crate::frames::Frames;
|
||||||
use crate::cpu::{Cycles};
|
use crate::cpu::Cycles;
|
||||||
use crate::ppu::{WIDTH, HEIGHT};
|
use crate::ppu::{WIDTH, HEIGHT};
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
|
141
src/sound.rs
141
src/sound.rs
@ -1,5 +1,10 @@
|
|||||||
|
use std::env;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use cpal::{Stream, StreamConfig, Device, Sample};
|
||||||
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use crate::cpu::Cycles;
|
use crate::cpu::Cycles;
|
||||||
|
use crate::utils::join_bytes;
|
||||||
|
|
||||||
pub const NR10_ADDRESS: u16 = 0xFF10;
|
pub const NR10_ADDRESS: u16 = 0xFF10;
|
||||||
pub const NR11_ADDRESS: u16 = 0xFF11;
|
pub const NR11_ADDRESS: u16 = 0xFF11;
|
||||||
@ -29,17 +34,144 @@ pub const NR52_ADDRESS: u16 = 0xFF26;
|
|||||||
|
|
||||||
pub const WAVE_PATTERN_RAM: RangeInclusive<u16> = 0xFF30..=0xFF3F;
|
pub const WAVE_PATTERN_RAM: RangeInclusive<u16> = 0xFF30..=0xFF3F;
|
||||||
|
|
||||||
|
const WAVE_DUTY_PATTERNS: [[u8; 8]; 4] = [
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 1],
|
||||||
|
[0, 0, 0, 0, 0, 0, 1, 1],
|
||||||
|
[0, 0, 0, 0, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 0, 0],
|
||||||
|
];
|
||||||
|
|
||||||
|
struct ChannelTwo {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
stream: Stream,
|
||||||
|
buffer: Arc<Mutex<Vec<f32>>>,
|
||||||
|
frequency_timer: u16,
|
||||||
|
duty_position: usize,
|
||||||
|
sample_timer: usize,
|
||||||
|
sample_rate: usize,
|
||||||
|
buffer_pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChannelTwo {
|
||||||
|
pub fn new(device: &Device, config: &StreamConfig, sample_rate: usize) -> Self {
|
||||||
|
let mut count: usize = 0;
|
||||||
|
// let mut count: f32 = 0.0;
|
||||||
|
let buffer = Arc::new(Mutex::new(vec![0.0; sample_rate]));
|
||||||
|
let buffer_clone = buffer.clone();
|
||||||
|
let stream = device.build_output_stream(&config, move |data: &mut [f32], _| {
|
||||||
|
/* for sample in data.iter_mut() {
|
||||||
|
let y: f32 = ((count / (sample_rate as f32)) * 440.0 * 2.0 * 3.14159).sin().clamp(-1.0, 1.0);
|
||||||
|
*sample = Sample::from(&y);
|
||||||
|
count += 1.0;
|
||||||
|
if count >= sample_rate as f32 {
|
||||||
|
count = 0.0;
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
let b = buffer_clone.lock().unwrap();
|
||||||
|
for sample in data.iter_mut() {
|
||||||
|
*sample = Sample::from(&b[count]);
|
||||||
|
count += 1;
|
||||||
|
if count >= b.len() {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, |err| eprintln!("An error occurred on the channel two: {}", err)).unwrap();
|
||||||
|
stream.play().unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
stream,
|
||||||
|
frequency_timer: 0,
|
||||||
|
duty_position: 0,
|
||||||
|
sample_timer: 0,
|
||||||
|
buffer_pos: 0,
|
||||||
|
sample_rate,
|
||||||
|
buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_buffer(&mut self, duty_pattern: u8) {
|
||||||
|
let sample = match WAVE_DUTY_PATTERNS[duty_pattern as usize][self.duty_position as usize] {
|
||||||
|
0 => -1.0,
|
||||||
|
1 => 1.0,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let clone = self.buffer.clone();
|
||||||
|
let mut buffer = clone.lock().unwrap();
|
||||||
|
buffer[self.buffer_pos] = sample;
|
||||||
|
|
||||||
|
self.buffer_pos += 1;
|
||||||
|
if self.buffer_pos >= buffer.len() {
|
||||||
|
self.buffer_pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cycle(&mut self, duty: u8, frequency: u16) {
|
||||||
|
self.frequency_timer = self.frequency_timer.saturating_sub(1);
|
||||||
|
if self.frequency_timer == 0 {
|
||||||
|
self.frequency_timer = (2048 - frequency) * 4;
|
||||||
|
self.duty_position += 1;
|
||||||
|
if self.duty_position > 7 {
|
||||||
|
self.duty_position = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sample_timer = self.sample_timer.saturating_add(self.sample_rate);
|
||||||
|
if self.sample_timer >= 4194304 {
|
||||||
|
self.update_buffer(duty);
|
||||||
|
self.sample_timer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Sound {
|
pub struct Sound {
|
||||||
io_registers: [u8; 48],
|
io_registers: [u8; 48],
|
||||||
|
channel_two: Option<ChannelTwo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Sine Wave
|
||||||
|
let channel_two = device.build_output_stream(&config, move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
||||||
|
for sample in data.iter_mut() {
|
||||||
|
let y: f32 = ((sound.count / (sample_rate as f32)) * 440.0 * 2.0 * 3.14159).sin().clamp(-1.0, 1.0);
|
||||||
|
*sample = Sample::from(&y);
|
||||||
|
sound.count += 1.0;
|
||||||
|
}
|
||||||
|
}, err_fn).unwrap();
|
||||||
|
channel_two.play().unwrap();
|
||||||
|
sound.channel_two = Some(channel_two); */
|
||||||
|
|
||||||
impl Sound {
|
impl Sound {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
if !env::var("SOUND_ENABLE").is_err() {
|
||||||
|
let host = cpal::default_host();
|
||||||
|
let device = host.default_output_device().expect("no output device available");
|
||||||
|
let mut supported_configs_range = device.supported_output_configs()
|
||||||
|
.expect("error while querying configs");
|
||||||
|
let supported_config = supported_configs_range.next()
|
||||||
|
.expect("no supported config?!")
|
||||||
|
.with_max_sample_rate();
|
||||||
|
// let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
|
||||||
|
let sample_rate = supported_config.sample_rate().0;
|
||||||
|
let config: StreamConfig = supported_config.into();
|
||||||
|
return Self {
|
||||||
|
io_registers: [0; 48],
|
||||||
|
channel_two: Some(ChannelTwo::new(&device, &config, sample_rate as usize)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
io_registers: [0; 48],
|
io_registers: [0; 48],
|
||||||
|
channel_two: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn channel_two_duty(&self) -> u8 {
|
||||||
|
(self.get_register(NR21_ADDRESS) >> 6) & 0b11
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn channel_two_frequency(&self) -> u16 {
|
||||||
|
join_bytes(self.get_register(NR24_ADDRESS), self.get_register(NR23_ADDRESS)) & 0x7F
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_io_register(address: u16) -> bool {
|
pub fn is_io_register(address: u16) -> bool {
|
||||||
address >= 0xFF10 && address <= 0xFF3F
|
address >= 0xFF10 && address <= 0xFF3F
|
||||||
}
|
}
|
||||||
@ -52,7 +184,7 @@ impl Sound {
|
|||||||
self.io_registers[(address - 0xFF10) as usize] = data;
|
self.io_registers[(address - 0xFF10) as usize] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_cycles(&self, cycles: Cycles) {
|
pub fn do_cycles(&mut self, cycles: Cycles) {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while count < cycles.0 {
|
while count < cycles.0 {
|
||||||
self.cycle();
|
self.cycle();
|
||||||
@ -60,6 +192,11 @@ impl Sound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cycle(&self) {
|
fn cycle(&mut self) {
|
||||||
|
if self.channel_two.is_some() {
|
||||||
|
let duty = self.channel_two_duty();
|
||||||
|
let frequency = self.channel_two_frequency();
|
||||||
|
self.channel_two.as_mut().unwrap().cycle(duty, frequency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::cpu::{Cycles};
|
use crate::cpu::Cycles;
|
||||||
use crate::interrupts::{Interrupt, Interrupts};
|
use crate::interrupts::{Interrupt, Interrupts};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
BitIndex,
|
BitIndex,
|
||||||
|
Loading…
Reference in New Issue
Block a user