-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathaudio.rs
327 lines (270 loc) · 12.5 KB
/
audio.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#[allow(unused_imports)]
use tracing::{debug, error, trace, info, warn};
use crate::*;
use mips::{InterruptUpdate, InterruptUpdateMode, IMask_AI};
use rcp::DmaInfo;
#[derive()]
pub struct AudioInterface {
comms: SystemCommunication,
// communication channel to be notified when DMA is complete
dma_completed_rx: mpsc::Receiver<DmaInfo>,
dma_completed_tx: mpsc::Sender<DmaInfo>,
// register state
dram_address: u32,
transfer_length: Option<u32>, // when Some(), a DMA is pending because a current buffer is currently playing
dma_enable: bool,
count: u32, // internal DAC counter
// buffer states
buffer_playing: bool,
next_buffer: Vec<f32>,
sample_rate: u32,
output_sample_rate: u32,
// Audio buffer queue
audio_queue: sdl2::audio::AudioQueue<f32>,
}
impl AudioInterface {
pub fn new(comms: SystemCommunication) -> Self {
let (dma_completed_tx, dma_completed_rx) = mpsc::channel();
let sdl_context = sdl2::init().unwrap();
let audio_subsystem = sdl_context.audio().unwrap();
//for driver in sdl2::audio::drivers() {
// println!("SDL audio driver: {}", driver);
//}
// TODO make this configurable in the UI
let desired_spec = sdl2::audio::AudioSpecDesired {
freq: Some(48000),
channels: Some(2),
samples: Some(256),
};
let audio_queue = audio_subsystem.open_queue(None, &desired_spec).unwrap();
let spec = audio_queue.spec();
info!(target: "AI", "Initialized audio freq={} channels={} format={:?}", spec.freq, spec.channels, spec.format);
Self {
comms: comms,
dma_completed_tx: dma_completed_tx,
dma_completed_rx: dma_completed_rx,
dram_address: 0,
transfer_length: None,
dma_enable: false,
count: 0,
buffer_playing: false,
next_buffer: Vec::new(),
sample_rate: 0,
output_sample_rate: spec.freq as u32,
audio_queue: audio_queue,
}
}
pub fn reset(&mut self) {
info!(target: "AI", "reset");
self.dram_address = 0;
self.transfer_length = None;
self.dma_enable = false;
self.count = 0;
self.buffer_playing = false;
}
pub fn step(&mut self) {
while let Ok(_) = self.dma_completed_rx.try_recv() {}
// if cpu_throttle is disabled, kill all the audio playback
// we still need to cycle next_buffer and trigger interrupts though
let can_play = self.comms.cpu_throttle.load(Ordering::Relaxed) == 1;
if !can_play {
self.audio_queue.clear();
self.audio_queue.pause();
}
// return if we don't need to be playing audio
if !self.dma_enable { return; }
// if the next buffer has been transfered over, go ahead and queue it up
// I don't think this will be a problem, but in the future we could check
// self.audio_queue.size() to fall below a certain threshold before uploading the
// buffer. But I think games won't be queuing audio faster than their playback
// framerate.
if self.next_buffer.len() > 0 && self.sample_rate > 0 {
// take the next buffer and replace it with an empty one
let buffer = std::mem::replace(&mut self.next_buffer, Vec::new());
if can_play {
// queue the data into SDL
let buffer_slice: &[f32] = &buffer;
self.audio_queue.queue_audio(&buffer_slice).unwrap();
// make sure playback is enabled
self.play();
//println!("queued up audio buffer sample count={}", buffer.len());
}
} else {
// there's no next buffer, technically audio could still be playing but we're not
// queuing audio data any more
self.buffer_playing = false;
}
// if a dma is pending, and no next buffer, do the dma and trigger AI interrupt
if self.next_buffer.len() == 0 && self.transfer_length.is_some() {
let len = std::mem::replace(&mut self.transfer_length, None).unwrap();
let source_buffer = {
let access = self.comms.rdram.read().unwrap();
let rdram: &[u32] = access.as_ref().unwrap();
let start = (self.dram_address >> 2) as usize;
let end = start + (len >> 2) as usize;
if end > rdram.len() {
panic!("read from ${:08X} length {} reads outside of RDRAM", start << 2, len);
}
rdram[start..end].to_owned()
};
self.next_buffer = self.resample_buffer(&source_buffer);
// mark system as playing
self.buffer_playing = true;
// AI interrupt triggers at the start of dma
self.comms.mi_interrupts_tx.as_ref().unwrap().send(InterruptUpdate(IMask_AI, InterruptUpdateMode::SetInterrupt)).unwrap();
}
}
// tell SDL to start playing
pub fn play(&self) {
self.audio_queue.resume();
}
// tell SDL to stop
pub fn pause(&self) {
self.audio_queue.pause();
}
fn resample_buffer(&self, buffer: &[u32]) -> Vec<f32> {
// convert the i16's (which are packed as u32's) into f32
// TODO maybe we can use output i16 format and let the hardware do the conversion
// but we're not processing /that/ much data.
let mut queue_buffer = Vec::new();
for v in buffer.iter() {
let mut left = ((v >> 16) as i16) as f32;
left = if left < 0.0 { left / 32768.0 } else { left / 32767.0 };
let mut right = ((v & 0xFFFF) as i16) as f32;
right = if right < 0.0 { right / 32768.0 } else { right / 32767.0 };
queue_buffer.push(left);
queue_buffer.push(right);
}
// resample the game audio to fit our output device
samplerate::convert(self.sample_rate, self.output_sample_rate, 2, samplerate::ConverterType::SincFastest, &queue_buffer).unwrap()
}
}
impl Addressable for AudioInterface {
fn read_u32(&mut self, offset: usize) -> Result<u32, ReadWriteFault> {
trace!(target: "AI", "read32 address=${:08X}", offset);
match offset {
// AI_DRAM_ADDR - returns AI_LENGTH
// AI_LENGTH
// AI_CONTROL - returns AI_LENGTH
0x0_0000 | 0x0_0004 | 0x0_0008 => {
Ok(self.audio_queue.size() * (std::mem::size_of::<f32>() as u32))
},
// AI_STATUS
0x0_000C => {
let busy = self.buffer_playing;
let full = busy && self.transfer_length.is_some();
let en = self.dma_enable;
let wc = false; // TODO
let bc = true; // TODO
let count = self.count;
Ok(((full as u32) << 31)
| ((busy as u32) << 30)
| ((en as u32) << 25)
| ((wc as u32) << 20)
| ((bc as u32) << 16)
| ((count & 0x3FFF) << 1)
| ((full as u32) << 0)
| 0x0110_0000)
},
_ => {
todo!();
},
}
}
fn write_u32(&mut self, value: u32, offset: usize) -> Result<WriteReturnSignal, ReadWriteFault> {
trace!(target: "AI", "write32 value=${:08X} address=${:08X}", value, offset);
match offset {
// AI_DRAM_ADDR
0x0_0000 => {
trace!(target: "AI", "write32 AI_DRAM_ADDR value=${:08X}", value);
self.dram_address = value & 0x00FFFFF8;
},
// AI_LENGTH
0x0_0004 => {
trace!(target: "AI", "write32 AI_LENGTH value=${:08X} (self.transfer_length={:?})", value, self.transfer_length);
let len = value & 0x0003_FFF8;
if len == 0 {
self.transfer_length = None;
} else {
match self.transfer_length {
Some(_) => { // overwrite queued length
self.transfer_length = Some(len);
},
None => { // No DMA pending, either queue or start dma
if self.buffer_playing || !self.dma_enable { // buffer playing or dma is disabled, queue size of later
//println!("new length={:?}", self.transfer_length);
self.transfer_length = Some(len);
// break current cpu run to process the buffer
self.comms.break_cpu();
} else {
// since the write_u32 call happens inside of a Cpu write, we'll
// queue DMA to process during the next RCP step
// no buffer playing, dma enabled, start transfer
let dma_info = DmaInfo {
initiator : "AI",
source_address: (self.dram_address),
dest_address : 0x045F_0000, // Special hacky address to capture the audio buffer
count : 1,
length : len,
source_stride : 0,
completed : Some(self.dma_completed_tx.clone()),
..Default::default()
};
// Set playing flag even though the dma hasn't technically started yet. this
// really means that a DMA is "in progress" and thus so is audio playback
self.buffer_playing = true;
// Queue DMA
self.comms.start_dma_tx.as_ref().unwrap().send(dma_info).unwrap();
// AI interrupt triggers at the start of dma
self.comms.mi_interrupts_tx.as_ref().unwrap().send(InterruptUpdate(IMask_AI, InterruptUpdateMode::SetInterrupt)).unwrap();
// Interrupt cpu to process things
self.comms.break_cpu();
}
}
}
}
},
// AI_CONTROL
0x0_0008 => {
trace!(target: "AI", "write32 AI_CONTROL value=${:08X}", value);
// if nothing is playing, reset state
if !self.dma_enable && !self.buffer_playing {
self.transfer_length = None; // next write to AI_LENGTH triggers dma, which starts playback
}
self.dma_enable = (value & 0x01) == 0x01;
},
// AI_STATUS
0x0_000C => {
// this register is read only, and writes ACK interrupts
trace!(target: "AI", "write32 AI_STATUS value=${:08X}", value);
self.comms.mi_interrupts_tx.as_ref().unwrap().send(InterruptUpdate(IMask_AI, InterruptUpdateMode::ClearInterrupt)).unwrap();
},
// AI_DACRATE
0x0_0010 => {
trace!(target: "AI", "write32 AI_DACRATE value=${:08X}", value);
self.sample_rate = 48726144 / (value + 1); // TODO needs to be adjusted to PAL?
info!(target: "AI", "setting DAC sample rate to {}", self.sample_rate);
},
// AI_BITRATE
0x0_0014 => {
trace!(target: "AI", "write32 AI_BITRATE value=${:08X}", value);
info!(target: "AI", "setting DAC bitrate to {}", 48726144 / (value + 1));
},
_ => {
todo!();
},
}
Ok(WriteReturnSignal::None)
}
fn write_block(&mut self, offset: usize, block: &[u32], _length: u32) -> Result<WriteReturnSignal, ReadWriteFault> {
if offset == 0x000F_0000 {
// Incoming buffer!
//println!("got block: {:?}", block);
assert!(self.next_buffer.len() == 0);
self.next_buffer = self.resample_buffer(block);
} else {
panic!("should never happen");
}
Ok(WriteReturnSignal::None)
}
}