game_toolkit_dosaudio/capture/
stream.rs1use super::{Capture, PortWrite};
2use crate::{Error, OplChip, Result, Tick};
3use rtrb::{Consumer, Producer};
4use std::sync::{
5 Arc,
6 atomic::{AtomicBool, AtomicU64, Ordering},
7};
8
9#[derive(Clone, Copy)]
10struct RegisterWrite {
11 at: Tick,
12 register: u16,
13 value: u8,
14}
15
16struct State {
17 stop: AtomicBool,
18 finished: AtomicBool,
19 overflowed: AtomicBool,
20 end: AtomicU64,
21}
22
23pub struct MusicCapture {
37 incoming: Consumer<RegisterWrite>,
38 state: Arc<State>,
39 chip: OplChip,
40 start: Tick,
41 writes: Vec<PortWrite>,
42 consumed: bool,
43}
44
45pub(crate) struct CaptureWriter {
46 outgoing: Producer<RegisterWrite>,
47 state: Arc<State>,
48}
49
50pub(crate) fn stream(
51 chip: OplChip,
52 start: Tick,
53 capacity: usize,
54) -> Result<(MusicCapture, CaptureWriter)> {
55 if capacity == 0 || capacity > 1_048_576 {
56 return Err(Error::Config(
57 "capture queue capacity must be 1..=1,048,576",
58 ));
59 }
60 let (outgoing, incoming) = rtrb::RingBuffer::new(capacity);
61 let state = Arc::new(State {
62 stop: AtomicBool::new(false),
63 finished: AtomicBool::new(false),
64 overflowed: AtomicBool::new(false),
65 end: AtomicU64::new(start.0),
66 });
67 Ok((
68 MusicCapture {
69 incoming,
70 state: Arc::clone(&state),
71 chip,
72 start,
73 writes: Vec::new(),
74 consumed: false,
75 },
76 CaptureWriter { outgoing, state },
77 ))
78}
79
80impl MusicCapture {
81 pub fn stop(&self) {
83 self.state.stop.store(true, Ordering::Release);
84 }
85
86 pub fn is_finished(&self) -> bool {
88 self.state.finished.load(Ordering::Acquire)
89 }
90
91 pub fn is_overflowed(&self) -> bool {
93 self.state.overflowed.load(Ordering::Acquire)
94 }
95
96 pub fn try_finish(&mut self) -> Result<Option<Capture>> {
102 if self.consumed {
103 return Ok(None);
104 }
105 let finished = self.is_finished();
108 while let Ok(write) = self.incoming.pop() {
109 if !self.is_overflowed() {
110 let address = if write.register & 0x100 == 0 {
111 0x388
112 } else {
113 0x38a
114 };
115 self.writes.extend([
116 PortWrite {
117 at: write.at,
118 port: address,
119 value: write.register as u8,
120 },
121 PortWrite {
122 at: write.at,
123 port: address + 1,
124 value: write.value,
125 },
126 ]);
127 }
128 }
129 if !finished {
130 return Ok(None);
131 }
132 self.consumed = true;
133 if self.is_overflowed() {
134 self.writes.clear();
135 return Err(Error::CaptureOverflow);
136 }
137 Ok(Some(Capture {
138 start: self.start,
139 end: Tick(self.state.end.load(Ordering::Acquire)),
140 chip: Some(self.chip),
141 address: [0; 2],
142 card: None,
143 writes: std::mem::take(&mut self.writes),
144 }))
145 }
146}
147
148impl Drop for MusicCapture {
149 fn drop(&mut self) {
150 self.stop();
151 }
152}
153
154impl CaptureWriter {
155 #[cfg(feature = "opl")]
156 pub(crate) fn record(&mut self, at: Tick, register: u16, value: u8) {
157 if self.is_finished()
158 || self.state.overflowed.load(Ordering::Acquire)
159 || self.outgoing.is_abandoned()
160 {
161 return;
162 }
163 if self
164 .outgoing
165 .push(RegisterWrite {
166 at,
167 register,
168 value,
169 })
170 .is_err()
171 {
172 self.state.overflowed.store(true, Ordering::Release);
173 }
174 }
175
176 pub(crate) fn boundary(&mut self, at: Tick) {
177 if self.is_finished() {
178 return;
179 }
180 self.state.end.store(at.0, Ordering::Relaxed);
181 if self.state.stop.load(Ordering::Acquire)
182 || self.state.overflowed.load(Ordering::Acquire)
183 || self.outgoing.is_abandoned()
184 {
185 self.state.finished.store(true, Ordering::Release);
186 }
187 }
188
189 pub(crate) fn is_finished(&self) -> bool {
190 self.state.finished.load(Ordering::Acquire)
191 }
192}