game_toolkit_dosaudio/
offline.rs1use crate::{
2 AudioEngine, DosAudio, Error, Frame, MusicHandle, PlayOptions, PreparedMusic, Result,
3 SoundConfig, Tick,
4};
5use std::time::Duration;
6
7pub struct Wav {
9 rate: u32,
10 frames: Vec<Frame>,
11}
12impl Wav {
13 pub fn sample_rate(&self) -> u32 {
15 self.rate
16 }
17 pub fn frames(&self) -> &[Frame] {
19 &self.frames
20 }
21 pub fn to_bytes(&self) -> Result<Vec<u8>> {
23 let length = self
24 .frames
25 .len()
26 .checked_mul(4)
27 .and_then(|n| u32::try_from(n).ok())
28 .ok_or(Error::TooLarge)?;
29 let riff_length = length.checked_add(36).ok_or(Error::TooLarge)?;
30 let mut bytes = Vec::with_capacity(
31 usize::try_from(length)
32 .map_err(|_| Error::TooLarge)?
33 .checked_add(44)
34 .ok_or(Error::TooLarge)?,
35 );
36 bytes.extend(b"RIFF");
37 bytes.extend(riff_length.to_le_bytes());
38 bytes.extend(b"WAVEfmt ");
39 bytes.extend(16u32.to_le_bytes());
40 bytes.extend(1u16.to_le_bytes());
41 bytes.extend(2u16.to_le_bytes());
42 bytes.extend(self.rate.to_le_bytes());
43 bytes.extend((self.rate * 4).to_le_bytes());
44 bytes.extend(4u16.to_le_bytes());
45 bytes.extend(16u16.to_le_bytes());
46 bytes.extend(b"data");
47 bytes.extend(length.to_le_bytes());
48 for frame in &self.frames {
49 for &value in frame {
50 let pcm = (value.clamp(-1.0, 1.0) * 32767.0).round() as i16;
51 bytes.extend(pcm.to_le_bytes());
52 }
53 }
54 Ok(bytes)
55 }
56}
57
58pub struct OfflineMachine {
64 control: DosAudio,
65 engine: AudioEngine,
66 frames: Vec<Frame>,
67}
68impl OfflineMachine {
69 pub fn new(config: SoundConfig, sample_rate: u32) -> Result<Self> {
71 let (control, engine) =
72 DosAudio::split(config.sample_rate(sample_rate).latency(Tick::ZERO))?;
73 Ok(Self {
74 control,
75 engine,
76 frames: Vec::new(),
77 })
78 }
79 pub fn now(&self) -> Tick {
81 self.control.now()
82 }
83 pub fn inb(&mut self, port: u16) -> u8 {
85 self.control.inb(port)
86 }
87 pub fn outb(&mut self, port: u16, value: u8) -> Result<()> {
89 self.control.try_outb_at(self.now(), port, value)
90 }
91 #[cfg(feature = "sb")]
93 pub fn next_dma_tick(&self) -> Option<Tick> {
94 self.control.next_dma_tick()
95 }
96 #[cfg(feature = "sb")]
98 pub fn dma_write(&mut self, channel: crate::DmaChannel, value: u8) -> Result<()> {
99 self.control.try_dma_write_at(self.now(), channel, value)
100 }
101 #[cfg(feature = "sb")]
103 pub fn dma_write16(&mut self, channel: crate::DmaChannel, value: u16) -> Result<()> {
104 self.control.try_dma_write16_at(self.now(), channel, value)
105 }
106 #[cfg(feature = "sb")]
108 pub fn attach_dma(&mut self, memory: std::sync::Arc<crate::ConventionalMemory>) -> Result<()> {
109 self.control.attach_dma(memory)
110 }
111 #[cfg(feature = "sb")]
113 pub fn poll_dma_completion(&mut self) -> Option<crate::DmaCompletion> {
114 self.control.poll_dma_completion()
115 }
116 #[cfg(feature = "sb")]
118 pub fn next_dma_block_tick(&self) -> Option<Tick> {
119 self.control.next_dma_block_tick()
120 }
121 #[cfg(feature = "sb")]
123 pub fn irq_request(&self) -> Option<crate::IrqRequest> {
124 crate::IoDevice::irq_request(&self.control, self.now())
125 }
126 pub fn sound(&mut self, hz: f64) -> Result<()> {
128 self.control.sound(hz)
129 }
130 pub fn nosound(&mut self) -> Result<()> {
132 self.control.nosound()
133 }
134 pub fn play(&mut self, sequence: impl Into<PreparedMusic>) -> Result<MusicHandle> {
136 self.engine
137 .play_at(sequence, PlayOptions::default(), self.now())
138 }
139 pub fn play_soundtrack(
141 &mut self,
142 soundtrack: std::sync::Arc<crate::Soundtrack>,
143 preferred: crate::MusicDevice,
144 options: crate::PlayOptions,
145 ) -> Result<MusicHandle> {
146 self.engine.play_soundtrack(soundtrack, preferred, options)
147 }
148
149 #[cfg(feature = "sb")]
152 pub fn play_pcm(
153 &mut self,
154 samples: impl Into<std::sync::Arc<[u8]>>,
155 sample_rate: u32,
156 ) -> Result<crate::SfxHandle> {
157 self.control.play_pcm(samples, sample_rate)
158 }
159 pub fn play_speaker(
161 &mut self,
162 music: impl Into<PreparedMusic>,
163 options: PlayOptions,
164 ) -> Result<crate::SpeakerHandle> {
165 self.engine.play_speaker(music, options)
166 }
167 pub fn advance_to(&mut self, at: Tick) -> Result<()> {
169 if at < self.now() {
170 return Err(Error::BackwardsTime);
171 }
172 let target = u128::from(at.0)
173 * u128::from(self.engine.sample_rate())
174 * u128::from(Tick::HZ_DENOMINATOR)
175 / u128::from(Tick::HZ_NUMERATOR);
176 let target = u64::try_from(target).map_err(|_| Error::TooLarge)?;
177 let extra = target.saturating_sub(self.engine.stats().frames);
178 let extra = usize::try_from(extra).map_err(|_| Error::TooLarge)?;
179 if self
181 .frames
182 .len()
183 .checked_add(extra)
184 .ok_or(Error::TooLarge)?
185 > (u32::MAX as usize - 36) / 4
186 {
187 return Err(Error::TooLarge);
188 }
189 loop {
190 #[cfg(feature = "sb")]
191 let next = self.control.next_pcm_tick().map_or(at, |next| next.min(at));
192 #[cfg(not(feature = "sb"))]
193 let next = at;
194 self.render_until(next);
197 self.control.try_advance_to(next)?;
198 if next == at {
199 break;
200 }
201 }
202 Ok(())
203 }
204
205 fn render_until(&mut self, at: Tick) {
206 let target = u128::from(at.0)
207 * u128::from(self.engine.sample_rate())
208 * u128::from(Tick::HZ_DENOMINATOR)
209 / u128::from(Tick::HZ_NUMERATOR);
210 let mut buffer = [[0.0; 2]; 512];
211 let mut remaining = (target as u64).saturating_sub(self.engine.stats().frames);
212 while remaining > 0 {
213 let count = remaining.min(buffer.len() as u64) as usize;
214 self.engine.render(&mut buffer[..count]);
215 self.frames.extend_from_slice(&buffer[..count]);
216 remaining -= count as u64;
217 }
218 }
219 pub fn render_for(&mut self, duration: Duration) -> Result<()> {
221 let delta = Tick::checked_from_duration(duration).ok_or(Error::TooLarge)?;
222 self.advance_to(self.now().checked_add(delta).ok_or(Error::TooLarge)?)
223 }
224 pub fn render_to_end(mut self) -> Result<Wav> {
228 #[cfg(feature = "sb")]
229 let pcm_end = self.control.pcm_end();
230 #[cfg(not(feature = "sb"))]
231 let pcm_end = None;
232 let until = self
233 .engine
234 .music_end()?
235 .into_iter()
236 .chain(pcm_end)
237 .chain(self.engine.speaker_end()?)
238 .max()
239 .ok_or(Error::Config("no music or PCM buffer is installed"))?
240 .checked_add(Tick::from_micros(250_000))
241 .ok_or(Error::TooLarge)?;
242 self.advance_to(until.max(self.now()))?;
243 Ok(self.finish())
244 }
245 pub fn finish(self) -> Wav {
247 Wav {
248 rate: self.engine.sample_rate(),
249 frames: self.frames,
250 }
251 }
252}