diff --git a/frontends/sdl/src/audio.rs b/frontends/sdl/src/audio.rs new file mode 100644 index 0000000000000000000000000000000000000000..7b9562cddf987356c2c06e5b795e0d18621a6d0e --- /dev/null +++ b/frontends/sdl/src/audio.rs @@ -0,0 +1,60 @@ +use sdl2::{ + audio::{AudioCallback, AudioSpecDesired}, + AudioSubsystem, Sdl, +}; +use std::time::Duration; + +struct SquareWave { + phase_inc: f32, + phase: f32, + volume: f32, +} + +impl AudioCallback for SquareWave { + type Channel = f32; + + fn callback(&mut self, out: &mut [f32]) { + for x in out.iter_mut() { + // this is a square wave with 50% of down + // and 50% of up values + *x = if self.phase <= 0.5 { + self.volume + } else { + -self.volume + }; + self.phase = (self.phase + self.phase_inc) % 1.0; + } + } +} + +pub struct Audio { + pub audio_subsystem: AudioSubsystem, +} + +impl Audio { + pub fn new(sdl: &Sdl) -> Self { + let audio_subsystem = sdl.audio().unwrap(); + + let desired_spec = AudioSpecDesired { + freq: Some(44100), + channels: Some(1), + samples: None, + }; + + let device = audio_subsystem + .open_playback(None, &desired_spec, |spec| SquareWave { + phase_inc: 440.0 / spec.freq as f32, + phase: 0.0, + volume: 0.25, + }) + .unwrap(); + + // starts the playback by resuming the audio + // device's activity + device.resume(); + + std::thread::sleep(Duration::from_millis(2000)); + + Self { audio_subsystem } + } +} diff --git a/frontends/sdl/src/util.rs b/frontends/sdl/src/graphics.rs similarity index 93% rename from frontends/sdl/src/util.rs rename to frontends/sdl/src/graphics.rs index df6a550f781e486ccdf4b4b914b1b6501ee131c4..0833705dd0af605f31e268e71ba708ec4976ef79 100644 --- a/frontends/sdl/src/util.rs +++ b/frontends/sdl/src/graphics.rs @@ -1,6 +1,6 @@ use sdl2::{ render::Canvas, rwops::RWops, surface::Surface, sys::image, ttf::Sdl2TtfContext, video::Window, - AudioSubsystem, EventPump, TimerSubsystem, VideoSubsystem, + AudioSubsystem, EventPump, Sdl, TimerSubsystem, VideoSubsystem, }; /// Structure that provides the complete set of Graphics @@ -19,10 +19,9 @@ impl Graphics { /// Start the SDL sub-system and all of its structure and returns /// a structure with all the needed stuff to handle SDL graphics /// and sound. - pub fn new(title: &str, width: u32, height: u32, scale: f32) -> Self { + pub fn new(sdl: &Sdl, title: &str, width: u32, height: u32, scale: f32) -> Self { // initializes the SDL sub-system, making it ready to be // used for display of graphics and audio - let sdl = sdl2::init().unwrap(); let video_subsystem = sdl.video().unwrap(); let timer_subsystem = sdl.timer().unwrap(); let audio_subsystem = sdl.audio().unwrap(); diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index 06fb9f67f2289691e4b15732cc7f133d92dce117..5b97892534924eb194b8377861533d0e850ba461 100644 --- a/frontends/sdl/src/main.rs +++ b/frontends/sdl/src/main.rs @@ -1,18 +1,18 @@ #![allow(clippy::uninlined_format_args)] +pub mod audio; pub mod data; -pub mod util; +pub mod graphics; +use audio::Audio; use boytacean::{ gb::GameBoy, pad::PadKey, ppu::{PaletteInfo, PpuMode, DISPLAY_HEIGHT, DISPLAY_WIDTH}, }; +use graphics::{surface_from_bytes, Graphics}; use sdl2::{event::Event, keyboard::Keycode, pixels::PixelFormatEnum}; use std::{cmp::max, time::SystemTime}; -use util::Graphics; - -use crate::util::surface_from_bytes; /// The scale at which the screen is going to be drawn /// meaning the ratio between Game Boy resolution and @@ -41,6 +41,7 @@ impl Default for Benchmark { pub struct Emulator { system: GameBoy, graphics: Graphics, + audio: Audio, logic_frequency: u32, visual_frequency: f32, next_tick_time: f32, @@ -51,14 +52,19 @@ pub struct Emulator { impl Emulator { pub fn new(system: GameBoy, screen_scale: f32) -> Self { + let sdl = sdl2::init().unwrap(); + let graphics = Graphics::new( + &sdl, + TITLE, + DISPLAY_WIDTH as u32, + DISPLAY_HEIGHT as u32, + screen_scale, + ); + let audio = Audio::new(&sdl); Self { system, - graphics: Graphics::new( - TITLE, - DISPLAY_WIDTH as u32, - DISPLAY_HEIGHT as u32, - screen_scale, - ), + graphics: graphics, + audio: audio, logic_frequency: GameBoy::CPU_FREQ, visual_frequency: GameBoy::VISUAL_FREQ, next_tick_time: 0.0,