Added binary to test simple synth sounds for ym2612

This commit is contained in:
transistor 2022-01-18 20:24:18 -08:00
parent 8cf8c07082
commit 749a9d2250
6 changed files with 249 additions and 80 deletions

View File

@ -240,6 +240,32 @@ before 2021-10-25
to more properly implement the priority shadow/highlight modes
Audio
-----
2021-12-12
- this is when I committed the audio support, but I'm not sure when I started. It was a little earlier
- cpal uses a callback to get the next buffer of data, so the buffer needs to be assembled outside of
the callback, with each device creating a Source with a buffer, that the mixer/output can draw upon
- initially I had it give an iterator to load the buffer, but that doesn't work for ym2612 generation
because it itself needs to mix a bunch of sources together to get the output buffer
- I made it use a circular buffer so that unused data can be skipped to keep the simulation in sync
- from various glitches I was able to get it to playback tones smoothly, with only the occasional pop
2022-01-17
- finally have done more, added the various register locations to set the frequency of the operators,
and added a way to combine the samples according to the algorithm of ops to get sound
- had to add `.reset()` to start from the beginning when a note is played, in order to prevent clicks
from the waveform all of a sudden jumping in level when the note starts
- started adding a binary to control just the ym2612 for testing, so I can isolate issues, and a lot
of minor issues have turned up
2022-01-18
- some kind of buffer problem causing clicking, where the waveform resets, possibly related to circular buf
- a quick attempt at fixing it shows that the audio source buffer is only copied to the mixer buffer
when it's written to the buffer (and overfills). Attempt to not write to the buffer means audio stops
when the source buffer is full

View File

@ -72,6 +72,14 @@ impl<T: Copy> CircularBuffer<T> {
}
}
pub fn free_space(&self) -> usize {
if self.out > self.inp {
self.out - self.inp - 1
} else {
self.buffer.len() - self.inp + self.out - 1
}
}
fn next_in(&self) -> usize {
if self.inp + 1 < self.buffer.len() {
self.inp + 1
@ -122,9 +130,14 @@ impl AudioSource {
}
}
pub fn space_available(&self) -> usize {
self.buffer.free_space()
}
pub fn fill_with(&mut self, buffer: &[f32]) {
for sample in buffer.iter() {
let sample = 0.25 * *sample;
// TODO this is here to keep it quiet for testing, but should be removed later
let sample = 0.5 * *sample;
self.buffer.insert(sample);
self.buffer.insert(sample);
if self.buffer.is_full() {
@ -156,6 +169,10 @@ impl Audio for AudioSource {
self.sample_rate
}
fn space_available(&self) -> usize {
self.space_available()
}
fn write_samples(&mut self, buffer: &[f32]) {
self.fill_with(buffer);
}

View File

@ -0,0 +1,103 @@
use moa_minifb;
use moa::peripherals::ym2612::{Ym2612};
use moa::error::Error;
use moa::system::System;
use moa::host::gfx::Frame;
use moa::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable, TransmutableBox, wrap_transmutable};
use moa::host::keys::{Key};
use moa::host::traits::{Host, HostData, WindowUpdater, KeyboardUpdater};
pub struct SynthControlsUpdater(HostData<bool>);
impl KeyboardUpdater for SynthControlsUpdater {
fn update_keyboard(&mut self, key: Key, state: bool) {
match key {
Key::Enter => { self.0.set(state); if state { println!("start"); } },
_ => { },
}
}
}
struct SynthControl {
button: HostData<bool>,
last: bool,
}
impl SynthControl {
pub fn new(button: HostData<bool>) -> Self {
Self {
button,
last: false,
}
}
}
impl Steppable for SynthControl {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let current = self.button.get();
if current != self.last {
self.last = current;
system.get_bus().write_u8(0x00, 0x28)?;
system.get_bus().write_u8(0x01, if current { 0xF0 } else { 0x00 })?;
}
Ok(1_000_000)
}
}
impl Transmutable for SynthControl {
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self)
}
}
fn set_register(device: &mut dyn Addressable, bank: u8, reg: u8, data: u8) -> Result<(), Error> {
let addr = (bank as Address) * 2;
device.write_u8(addr, reg)?;
device.write_u8(addr + 1, data)?;
Ok(())
}
fn initialize_ym(ym_sound: TransmutableBox) -> Result<(), Error> {
let mut borrow = ym_sound.borrow_mut();
let device = borrow.as_addressable().unwrap();
set_register(device, 0, 0x30, 0x71)?;
set_register(device, 0, 0x34, 0x0D)?;
set_register(device, 0, 0x38, 0x33)?;
set_register(device, 0, 0x3C, 0x01)?;
set_register(device, 0, 0xA4, 0x22)?;
set_register(device, 0, 0xA0, 0x69)?;
set_register(device, 0, 0xB0, 0x30)?;
Ok(())
}
fn main() {
let matches = moa_minifb::new("YM2612 Tester/Synth")
.get_matches();
moa_minifb::run(matches, |host| {
let mut system = System::new();
let button = HostData::new(false);
let control = wrap_transmutable(SynthControl::new(button.clone()));
system.add_device("control", control)?;
let ym_sound = wrap_transmutable(Ym2612::create(host)?);
initialize_ym(ym_sound.clone())?;
system.add_addressable_device(0x00, ym_sound)?;
let frame = Frame::new_shared(384, 128);
host.add_window(Frame::new_updater(frame.clone()))?;
host.register_keyboard(Box::new(SynthControlsUpdater(button)))?;
Ok(system)
});
}

View File

@ -17,6 +17,10 @@ impl SineWave {
position: 0,
}
}
pub fn reset(&mut self) {
self.position = 0;
}
}
impl Iterator for SineWave {
@ -44,6 +48,10 @@ impl SquareWave {
position: 0,
}
}
pub fn reset(&mut self) {
self.position = 0;
}
}
impl Iterator for SquareWave {

View File

@ -49,6 +49,7 @@ pub trait KeyboardUpdater: Send {
pub trait Audio {
fn samples_per_second(&self) -> usize;
fn space_available(&self) -> usize;
fn write_samples(&mut self, buffer: &[f32]);
}

View File

@ -42,10 +42,19 @@ impl Operator {
self.wave.frequency = frequency * self.multiplier;
}
pub fn reset(&mut self) {
self.wave.reset();
}
pub fn set_multiplier(&mut self, frequency: f32, multiplier: f32) {
self.multiplier = multiplier;
self.set_frequency(frequency);
}
pub fn get_sample(&mut self) -> f32 {
// TODO this would need to take into account the volume and envelope
self.wave.next().unwrap()
}
}
#[derive(Clone)]
@ -73,52 +82,55 @@ impl Channel {
}
}
pub fn reset(&mut self) {
for operator in self.operators.iter_mut() {
operator.reset();
}
}
pub fn get_sample(&mut self) -> f32 {
match self.algorithm {
OperatorAlgorithm::A0 => {
let mut sample = 0.0;
sample *= self.operators[0].wave.next().unwrap();
sample *= self.operators[1].wave.next().unwrap();
sample *= self.operators[2].wave.next().unwrap();
sample *= self.operators[3].wave.next().unwrap();
sample
self.operators[0].get_sample()
* self.operators[1].get_sample()
* self.operators[2].get_sample()
* self.operators[3].get_sample()
},
OperatorAlgorithm::A1 => {
let mut sample = self.operators[0].wave.next().unwrap() + self.operators[1].wave.next().unwrap();
sample *= self.operators[2].wave.next().unwrap();
sample *= self.operators[3].wave.next().unwrap();
sample
let sample1 = (self.operators[0].get_sample() + self.operators[1].get_sample()) / 2.0;
let sample2 = self.operators[2].get_sample();
let sample3 = self.operators[3].get_sample();
sample1 * sample2 * sample3
},
OperatorAlgorithm::A2 => {
let mut sample = self.operators[0].wave.next().unwrap() + (self.operators[1].wave.next().unwrap() * self.operators[2].wave.next().unwrap());
sample *= self.operators[3].wave.next().unwrap();
let mut sample = (self.operators[0].get_sample() + (self.operators[1].get_sample() * self.operators[2].get_sample())) / 2.0;
sample *= self.operators[3].get_sample();
sample
},
OperatorAlgorithm::A3 => {
let mut sample = (self.operators[0].wave.next().unwrap() * self.operators[1].wave.next().unwrap()) + self.operators[2].wave.next().unwrap();
sample *= self.operators[3].wave.next().unwrap();
let mut sample = ((self.operators[0].get_sample() * self.operators[1].get_sample()) + self.operators[2].get_sample()) / 2.0;
sample *= self.operators[3].get_sample();
sample
},
OperatorAlgorithm::A4 => {
let sample1 = self.operators[0].wave.next().unwrap() * self.operators[1].wave.next().unwrap();
let sample2 = self.operators[2].wave.next().unwrap() * self.operators[3].wave.next().unwrap();
sample1 + sample2
let sample1 = self.operators[0].get_sample() * self.operators[1].get_sample();
let sample2 = self.operators[2].get_sample() * self.operators[3].get_sample();
(sample1 + sample2) / 2.0
},
OperatorAlgorithm::A5 => {
let sample1 = self.operators[0].wave.next().unwrap();
let sample2 = self.operators[1].wave.next().unwrap() + self.operators[2].wave.next().unwrap() + self.operators[3].wave.next().unwrap();
let sample1 = self.operators[0].get_sample();
let sample2 = (self.operators[1].get_sample() + self.operators[2].get_sample() + self.operators[3].get_sample()) / 3.0;
sample1 * sample2
},
OperatorAlgorithm::A6 => {
let sample1 = self.operators[0].wave.next().unwrap() * self.operators[1].wave.next().unwrap();
sample1 + self.operators[2].wave.next().unwrap() + self.operators[3].wave.next().unwrap()
let sample1 = self.operators[0].get_sample() * self.operators[1].get_sample();
(sample1 + self.operators[2].get_sample() + self.operators[3].get_sample()) / 3.0
},
OperatorAlgorithm::A7 => {
let mut sample = 0.0;
sample += self.operators[0].wave.next().unwrap();
sample += self.operators[1].wave.next().unwrap();
sample += self.operators[2].wave.next().unwrap();
sample += self.operators[3].wave.next().unwrap();
let sample = self.operators[0].get_sample()
+ self.operators[1].get_sample()
+ self.operators[2].get_sample()
+ self.operators[3].get_sample();
sample / 4.0
},
}
@ -150,45 +162,43 @@ impl Ym2612 {
}
pub fn set_register(&mut self, bank: usize, reg: usize, data: u8) {
match reg {
0x28 => {
let ch = (data as usize) & 0x07;
self.channels[ch].on = data >> 4;
println!("Note: {}: {:x}", ch, self.channels[ch].on);
},
0x30 => {
let (ch, op) = get_ch_op(bank, reg);
let multiplier = if data == 0 { 0.5 } else { (data & 0x0F) as f32 };
let frequency = self.channels[ch].base_frequency;
self.channels[ch].operators[op].set_multiplier(frequency, multiplier)
},
0xA4 | 0xA5 | 0xA6 => {
let ch = (reg & 0x07) - 4 + (bank * 3);
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF) | ((data as u16) & 0x07) << 8;
self.channel_frequencies[ch].0 = (data & 0x1C) >> 3;
},
0xA0 | 0xA1 | 0xA2 => {
let ch = (reg & 0x07) + (bank * 3);
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF00) | data as u16;
if reg == 0x28 {
let ch = (data as usize) & 0x07;
self.channels[ch].on = data >> 4;
self.channels[ch].reset();
println!("Note: {}: {:x}", ch, self.channels[ch].on);
} else if (reg & 0xF0) == 0x30 {
let (ch, op) = get_ch_op(bank, reg);
let multiplier = if data == 0 { 0.5 } else { (data & 0x0F) as f32 };
let frequency = self.channels[ch].base_frequency;
debug!("{}: channel {} operator {} set to multiplier {}", DEV_NAME, ch + 1, op + 1, multiplier);
self.channels[ch].operators[op].set_multiplier(frequency, multiplier)
} else if reg >= 0xA4 && reg <= 0xA6 {
let ch = (reg & 0x07) - 4 + (bank * 3);
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF) | ((data as u16) & 0x07) << 8;
self.channel_frequencies[ch].0 = (data & 0x38) >> 3;
} else if reg >= 0xA0 && reg <= 0xA2 {
let ch = (reg & 0x07) + (bank * 3);
self.channel_frequencies[ch].1 = (self.channel_frequencies[ch].1 & 0xFF00) | data as u16;
let frequency = fnumber_to_frequency(self.channel_frequencies[ch]);
self.channels[ch].set_frequency(frequency);
},
0xB0 | 0xB1 | 0xB2 => {
let ch = (reg & 0x07) + (bank * 3);
self.channels[ch].algorithm = match data & 0x07 {
0 => OperatorAlgorithm::A0,
1 => OperatorAlgorithm::A1,
2 => OperatorAlgorithm::A2,
3 => OperatorAlgorithm::A3,
4 => OperatorAlgorithm::A4,
5 => OperatorAlgorithm::A5,
6 => OperatorAlgorithm::A6,
7 => OperatorAlgorithm::A7,
_ => OperatorAlgorithm::A0,
};
},
_ => warning!("{}: !!! unhandled write to register {:0x} with {:0x}", DEV_NAME, reg, data),
let frequency = fnumber_to_frequency(self.channel_frequencies[ch]);
debug!("{}: channel {} set to frequency {}", DEV_NAME, ch + 1, frequency);
self.channels[ch].set_frequency(frequency);
} else if reg >= 0xB0 && reg <= 0xB2 {
let ch = (reg & 0x07) + (bank * 3);
self.channels[ch].algorithm = match data & 0x07 {
0 => OperatorAlgorithm::A0,
1 => OperatorAlgorithm::A1,
2 => OperatorAlgorithm::A2,
3 => OperatorAlgorithm::A3,
4 => OperatorAlgorithm::A4,
5 => OperatorAlgorithm::A5,
6 => OperatorAlgorithm::A6,
7 => OperatorAlgorithm::A7,
_ => OperatorAlgorithm::A0,
};
} else {
warning!("{}: !!! unhandled write to register {:0x} with {:0x}", DEV_NAME, reg, data);
}
}
}
@ -224,24 +234,28 @@ impl Steppable for Ym2612 {
// self.source.write_samples(rate / 1000, &mut self.sine);
//}
let rate = self.source.samples_per_second() / 1000;
let mut buffer = vec![0.0; rate];
for i in 0..rate {
let mut sample = 0.0;
let mut count = 0;
let rate = self.source.samples_per_second();
let available = self.source.space_available();
let samples = if available < rate / 1000 { available } else { rate / 1000 };
//if self.source.space_available() >= samples {
let mut buffer = vec![0.0; samples];
for i in 0..samples {
let mut sample = 0.0;
let mut count = 0;
for ch in 0..7 {
if self.channels[ch].on != 0 {
sample += self.channels[ch].get_sample();
count += 1;
for ch in 0..7 {
if self.channels[ch].on != 0 {
sample += self.channels[ch].get_sample();
count += 1;
}
}
if count > 0 {
buffer[i] = sample / count as f32;
}
}
if count > 0 {
buffer[i] = sample / count as f32;
}
}
self.source.write_samples(&buffer);
self.source.write_samples(&buffer);
//}
Ok(1_000_000) // Every 1ms of simulated time
}