1use crate::bus::{Command, TimedCommand};
2#[cfg(feature = "opl")]
3use crate::device::opl::OplVoice;
4#[cfg(feature = "psg")]
5use crate::device::psg::PsgVoice;
6#[cfg(feature = "sb")]
7use crate::device::sb::{DacState, SbControl};
8use crate::device::speaker::SpeakerVoice;
9#[cfg(feature = "mpu401")]
10use crate::midi::voice::MidiVoice;
11use crate::music::{MusicCapabilities, MusicRequest, MusicRuntime, Player, validate_play};
12use crate::{
13 Error, Frame, MusicEvent, MusicHandle, MusicTarget, PlayOptions, PreparedMusic, Result,
14 SoundConfig, Tick,
15};
16use rtrb::Consumer;
17
18#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
20pub struct EngineStats {
21 pub frames: u64,
23 pub late_commands: u64,
25}
26
27pub struct AudioEngine {
34 rate: u32,
35 latency: Tick,
36 native: bool,
37 commands: Consumer<TimedCommand>,
38 speaker: Option<SpeakerVoice>,
39 #[cfg(feature = "mpu401")]
40 midi: Option<MidiVoice>,
41 #[cfg(feature = "psg")]
42 psg: Option<PsgVoice>,
43 #[cfg(feature = "opl")]
44 opl: Option<OplVoice>,
45 #[cfg(feature = "sb")]
46 dac: Option<DacState>,
47 #[cfg(feature = "sb")]
48 dac_filter: [f64; 2],
49 player: Option<Player>,
50 runtime: Option<MusicRuntime>,
51 speaker_player: Option<Player>,
52 speaker_runtime: Option<MusicRuntime>,
53 capture: Option<crate::capture::stream::CaptureWriter>,
54 event_time: Tick,
55 stats: EngineStats,
56}
57
58impl AudioEngine {
59 pub(crate) fn new(config: &SoundConfig, commands: Consumer<TimedCommand>) -> Result<Self> {
60 #[cfg(feature = "mpu401")]
61 let midi = config
62 .midi
63 .as_ref()
64 .map(|backend| MidiVoice::new(backend, config.sample_rate))
65 .transpose()?;
66 Ok(Self {
67 rate: config.sample_rate,
68 latency: config.latency,
69 native: config.native,
70 commands,
71 #[cfg(feature = "mpu401")]
72 midi,
73 #[cfg(feature = "psg")]
74 psg: config.psg.then(PsgVoice::new),
75 speaker: config
76 .speaker
77 .then(|| SpeakerVoice::new(config.sample_rate)),
78 #[cfg(feature = "opl")]
79 opl: config
80 .opl
81 .map(|chip| OplVoice::new(chip, config.backend.clone())),
82 #[cfg(feature = "sb")]
83 dac: config
84 .sb
85 .map(|resources| SbControl::new(resources, config.sb_model, config.dma16).output()),
86 #[cfg(feature = "sb")]
87 dac_filter: [0.0; 2],
88 player: None,
89 runtime: None,
90 speaker_player: None,
91 speaker_runtime: None,
92 capture: None,
93 event_time: Tick::ZERO,
94 stats: EngineStats::default(),
95 })
96 }
97
98 pub fn sample_rate(&self) -> u32 {
100 self.rate
101 }
102 pub fn now(&self) -> Tick {
104 Tick(
105 (u128::from(self.stats.frames) * u128::from(Tick::HZ_NUMERATOR) / self.denominator())
106 as u64,
107 )
108 }
109 pub fn stats(&self) -> EngineStats {
111 self.stats
112 }
113
114 pub fn midi_dropped_messages(&self) -> u64 {
116 #[cfg(feature = "mpu401")]
117 if let Some(midi) = &self.midi {
118 return midi.core.dropped_messages();
119 }
120 0
121 }
122
123 pub fn start_capture(&mut self, capacity: usize) -> Result<crate::MusicCapture> {
127 if self
128 .capture
129 .as_ref()
130 .is_some_and(|writer| !writer.is_finished())
131 {
132 return Err(Error::Config("synthesis capture is already active"));
133 }
134 #[cfg(feature = "opl")]
135 let chip = self.opl.as_ref().map(|voice| voice.chip);
136 #[cfg(not(feature = "opl"))]
137 let chip: Option<crate::OplChip> = None;
138 let chip = chip.ok_or(Error::Config("synthesis capture requires OPL hardware"))?;
139 let (capture, writer) = crate::capture::stream::stream(chip, self.now(), capacity)?;
140 self.capture = Some(writer);
141 Ok(capture)
142 }
143
144 pub fn music_controller(&mut self, capacity: usize) -> Result<crate::MusicController> {
147 if self.runtime.is_some() {
148 return Err(Error::Config("a music controller already exists"));
149 }
150 let capabilities = self.capabilities();
151 if capabilities.opl.is_none()
152 && !capabilities.midi
153 && !capabilities.psg
154 && !capabilities.speaker
155 && !capabilities.native
156 {
157 return Err(Error::Config("runtime music requires installed hardware"));
158 }
159 let (controller, runtime) =
160 crate::MusicController::new(capabilities, self.rate, self.now(), capacity)?;
161 self.runtime = Some(runtime);
162 Ok(controller)
163 }
164
165 pub fn play_speaker(
169 &mut self,
170 music: impl Into<PreparedMusic>,
171 options: PlayOptions,
172 ) -> Result<crate::SpeakerHandle> {
173 self.play_speaker_at(music, options, self.now())
174 }
175
176 pub fn play_speaker_at(
179 &mut self,
180 music: impl Into<PreparedMusic>,
181 options: PlayOptions,
182 at: Tick,
183 ) -> Result<crate::SpeakerHandle> {
184 if at < self.now() {
185 return Err(Error::BackwardsTime);
186 }
187 let music = music.into();
188 if music.target != MusicTarget::Speaker {
189 return Err(Error::Config("speaker playback requires speaker events"));
190 }
191 validate_play(self.capabilities(), &music, options, at)?;
192 if let Some(old) = self.speaker_player.take() {
193 old.state
194 .finished
195 .store(true, std::sync::atomic::Ordering::Release);
196 }
197 let handle = MusicHandle::new(options.volume, self.rate);
198 self.speaker_player = Some(Player::new(music, options, &handle, at, self.now()));
199 Ok(crate::SpeakerHandle::new(handle))
200 }
201
202 pub fn speaker_controller(&mut self, capacity: usize) -> Result<crate::MusicController> {
204 if self.speaker.is_none() || self.speaker_runtime.is_some() {
205 return Err(Error::Config(
206 "speaker is absent or its controller already exists",
207 ));
208 }
209 let capabilities = MusicCapabilities {
210 opl: None,
211 midi: false,
212 midi_device: None,
213 native: false,
214 psg: false,
215 speaker: true,
216 };
217 let (controller, runtime) =
218 crate::MusicController::new(capabilities, self.rate, self.now(), capacity)?;
219 self.speaker_runtime = Some(runtime);
220 Ok(controller)
221 }
222
223 pub(crate) fn speaker_end(&self) -> Result<Option<Tick>> {
224 let Some(player) = &self.speaker_player else {
225 return Ok(None);
226 };
227 if player.options.looped {
228 return Err(Error::Config(
229 "render_to_end requires finite speaker playback",
230 ));
231 }
232 player
233 .origin
234 .checked_add(player.sequence.duration)
235 .map(Some)
236 .ok_or(Error::TooLarge)
237 }
238
239 fn speaker_next(&self, at: Tick) -> Option<Tick> {
240 let sequence = self.speaker_player.as_ref().and_then(Player::next);
241 let runtime = self
242 .speaker_runtime
243 .as_ref()
244 .and_then(|runtime| runtime.next(at, self.speaker_player.is_some()));
245 sequence.into_iter().chain(runtime).min()
246 }
247
248 fn speaker_due(&mut self, at: Tick) -> bool {
249 if self.speaker_player.is_none() && self.speaker_runtime.is_none() {
250 return false;
251 }
252 std::mem::swap(&mut self.player, &mut self.speaker_player);
253 std::mem::swap(&mut self.runtime, &mut self.speaker_runtime);
254 let changed = self.runtime_due(at);
255 self.sequence_due(at);
256 std::mem::swap(&mut self.player, &mut self.speaker_player);
257 std::mem::swap(&mut self.runtime, &mut self.speaker_runtime);
258 changed
259 }
260
261 pub fn play(
268 &mut self,
269 sequence: impl Into<PreparedMusic>,
270 options: PlayOptions,
271 ) -> Result<MusicHandle> {
272 self.play_at(sequence, options, self.now())
273 }
274
275 pub fn play_at(
277 &mut self,
278 sequence: impl Into<PreparedMusic>,
279 options: PlayOptions,
280 at: Tick,
281 ) -> Result<MusicHandle> {
282 if at < self.now() {
283 return Err(Error::BackwardsTime);
284 }
285 let sequence = sequence.into();
286 validate_play(self.capabilities(), &sequence, options, at)?;
287 self.event_time = self.now();
288 self.release_notes();
289 let handle = MusicHandle::new(options.volume, self.rate);
290 let now = self.now();
291 self.player = Some(Player::new(sequence, options, &handle, at, now));
292 Ok(handle)
293 }
294
295 pub(crate) fn music_end(&self) -> Result<Option<Tick>> {
296 let Some(player) = self.player.as_ref() else {
297 return Ok(None);
298 };
299 if player.options.looped {
300 return Err(Error::Config(
301 "offline render_to_end requires non-looping music",
302 ));
303 }
304 player
305 .origin
306 .checked_add(player.sequence.duration)
307 .map(Some)
308 .ok_or(Error::TooLarge)
309 }
310
311 pub fn play_soundtrack(
313 &mut self,
314 soundtrack: std::sync::Arc<crate::Soundtrack>,
315 preferred: crate::MusicDevice,
316 options: PlayOptions,
317 ) -> Result<MusicHandle> {
318 let (handle, player) = crate::music::soundtrack::player(
319 soundtrack,
320 preferred,
321 self.capabilities(),
322 self.rate,
323 options,
324 self.now(),
325 self.now(),
326 )?;
327 self.release_notes();
328 if let Some(old) = self.player.replace(player) {
329 old.state
330 .finished
331 .store(true, std::sync::atomic::Ordering::Release);
332 }
333 Ok(handle)
334 }
335
336 fn switch_music_device(&mut self) {
337 use std::sync::atomic::Ordering;
338 let Some(player) = &self.player else {
339 return;
340 };
341 let requested = player.state.switch_request.swap(u32::MAX, Ordering::AcqRel);
342 let Some(device) = crate::MusicDevice::from_code(requested) else {
343 return;
344 };
345 if player.ended || player.state.device.load(Ordering::Relaxed) == requested {
346 return;
347 }
348 let position = Tick(
349 self.event_time
350 .0
351 .saturating_sub(player.origin.0)
352 .min(player.sequence.duration.0),
353 );
354 self.release_notes();
355 let player = self.player.as_mut().expect("active soundtrack");
356 let arrangement = player
357 .soundtrack
358 .as_ref()
359 .expect("switchable handle")
360 .find(device)
361 .expect("validated route");
362 let checkpoint = arrangement
363 .checkpoints
364 .partition_point(|c| c.at <= position)
365 .saturating_sub(1);
366 player.sequence = arrangement.music.clone();
367 player.index = arrangement.checkpoints[checkpoint].index;
368 let len = arrangement.checkpoints[checkpoint].events.len();
369 #[cfg(feature = "opl")]
370 if matches!(
371 self.player.as_ref().unwrap().sequence.target,
372 MusicTarget::Opl(crate::OplChip::Opl2)
373 ) && self
374 .opl
375 .as_ref()
376 .is_some_and(|o| o.chip == crate::OplChip::Opl3)
377 {
378 self.write_opl(0x105, 0);
379 }
380
381 for index in 0..len {
382 let event = self
383 .player
384 .as_ref()
385 .unwrap()
386 .soundtrack
387 .as_ref()
388 .unwrap()
389 .find(device)
390 .unwrap()
391 .checkpoints[checkpoint]
392 .events[index];
393 self.music_event(event);
394 }
395 self.player
396 .as_ref()
397 .unwrap()
398 .state
399 .device
400 .store(requested, Ordering::Release);
401 }
402
403 fn native_sample(&self, start: u128, denominator: u128) -> Frame {
404 let Some(player) = &self.player else {
405 return [0.0; 2];
406 };
407 if player
408 .state
409 .stopped
410 .load(std::sync::atomic::Ordering::Relaxed)
411 || player.was_paused
412 {
413 return [0.0; 2];
414 }
415 let Some(pcm) = &player.sequence.pcm else {
416 return [0.0; 2];
417 };
418 let elapsed = start.saturating_sub(u128::from(player.origin.0) * denominator);
419 let frame = elapsed * u128::from(pcm.rate) * u128::from(Tick::HZ_DENOMINATOR)
420 / (denominator * u128::from(Tick::HZ_NUMERATOR));
421 pcm.frames.get(frame as usize).copied().unwrap_or([0.0; 2])
422 }
423 fn soundtrack_mutes(&self, target: MusicTarget) -> bool {
424 self.player.as_ref().is_some_and(|p| {
425 p.soundtrack.is_some()
426 && p.sequence.target != target
427 && !(matches!(target, MusicTarget::Opl(_))
428 && matches!(p.sequence.target, MusicTarget::Opl(_)))
429 })
430 }
431
432 fn denominator(&self) -> u128 {
433 u128::from(self.rate) * u128::from(Tick::HZ_DENOMINATOR)
434 }
435
436 fn capabilities(&self) -> MusicCapabilities {
437 #[cfg(feature = "opl")]
438 let opl = self.opl.as_ref().map(|voice| voice.chip);
439 #[cfg(not(feature = "opl"))]
440 let opl = None;
441 MusicCapabilities {
442 opl,
443 midi: {
444 #[cfg(feature = "mpu401")]
445 {
446 self.midi.is_some()
447 }
448 #[cfg(not(feature = "mpu401"))]
449 {
450 false
451 }
452 },
453 midi_device: {
454 #[cfg(feature = "mpu401")]
455 {
456 self.midi.as_ref().map(|m| m.device)
457 }
458 #[cfg(not(feature = "mpu401"))]
459 {
460 None
461 }
462 },
463 psg: {
464 #[cfg(feature = "psg")]
465 {
466 self.psg.is_some()
467 }
468 #[cfg(not(feature = "psg"))]
469 {
470 false
471 }
472 },
473 speaker: self.speaker.is_some(),
474 native: self.native,
475 }
476 }
477
478 fn runtime_next(&self, now: Tick) -> Option<Tick> {
479 self.runtime
480 .as_ref()
481 .and_then(|runtime| runtime.next(now, self.player.is_some()))
482 }
483
484 fn runtime_due(&mut self, at: Tick) -> bool {
485 let mut changed = false;
486 while self.runtime_next(at).is_some_and(|next| next <= at) {
487 let request = self.runtime.as_mut().expect("scheduled runtime").pop();
488 if request.at.is_some_and(|stamp| stamp < at) {
489 self.stats.late_commands += 1;
490 }
491 match request.request {
492 MusicRequest::Event(event) => self.music_event(event),
493 MusicRequest::Replace(mut player) => {
494 self.release_notes();
495 if let Some(old) = self.player.take() {
496 old.state
497 .finished
498 .store(true, std::sync::atomic::Ordering::Release);
499 self.runtime
500 .as_mut()
501 .expect("scheduled runtime")
502 .retire(old);
503 }
504 player.origin = at;
505 player.last = at;
506 if at.checked_add(player.sequence.duration).is_none() {
507 player.ended = true;
508 player
509 .state
510 .finished
511 .store(true, std::sync::atomic::Ordering::Release);
512 }
513 player.prepare(at);
514 self.player = Some(player);
515 self.switch_music_device();
516 self.sequence_due(at);
517 changed = true;
518 }
519 }
520 }
521 changed
522 }
523
524 fn sequence_due(&mut self, at: Tick) {
525 while self
526 .player
527 .as_ref()
528 .and_then(Player::next)
529 .is_some_and(|next| next <= at)
530 {
531 let player = self.player.as_mut().expect("scheduled player");
532 let event = player.pop();
533 let ended = player.ended;
534 match event {
536 Some(MusicEvent::Speaker { divisor, enabled }) => {
537 let divisor = match (enabled, divisor) {
538 (false, _) => 0,
539 (true, 0) => 65_536,
540 (true, divisor) => u32::from(divisor),
541 };
542 player
543 .state
544 .tone
545 .store(divisor, std::sync::atomic::Ordering::Relaxed);
546 }
547 Some(MusicEvent::SpeakerLevel(_)) => player
548 .state
549 .tone
550 .store(0, std::sync::atomic::Ordering::Relaxed),
551 _ => {}
552 }
553 if let Some(event) = event {
554 self.music_event(event);
555 }
556 if ended {
557 self.release_notes();
558 }
559 }
560 }
561 fn command_time(&self, command: &TimedCommand) -> Tick {
562 command.at.checked_add(self.latency).unwrap_or(Tick::MAX)
563 }
564
565 fn apply(&mut self, command: Command) {
566 match command {
567 #[cfg(feature = "mpu401")]
568 Command::MidiByte(byte) => {
569 if let Some(midi) = &mut self.midi {
570 midi.byte(byte);
571 }
572 }
573 #[cfg(feature = "psg")]
574 Command::Psg(byte) => {
575 if let Some(psg) = &mut self.psg {
576 psg.write(byte);
577 }
578 }
579
580 #[cfg(feature = "sb")]
581 Command::Dac(state) => self.dac = Some(state),
582 Command::Speaker(mut state) => {
583 state.epoch = state.epoch.checked_add(self.latency).unwrap_or(Tick::MAX);
584 if let Some(speaker) = &mut self.speaker {
585 speaker.state = state;
586 }
587 }
588 #[cfg(feature = "opl")]
589 Command::Opl { register, value } => {
590 self.write_opl(register, value);
591 }
592 #[cfg(feature = "opl")]
593 Command::OplPair { first, second } => {
594 self.write_opl(first.0, first.1);
595 self.write_opl(second.0, second.1);
596 }
597 }
598 }
599
600 fn music_event(&mut self, event: MusicEvent) {
601 match event {
602 MusicEvent::SpeakerLevel(high) => {
603 if let Some(speaker) = &mut self.speaker {
604 speaker.state.gate = false;
605 speaker.state.data = high;
606 speaker.state.epoch = self.event_time;
607 }
608 }
609
610 MusicEvent::Speaker { divisor, enabled } => {
611 if let Some(speaker) = &mut self.speaker {
612 speaker.state.divisor = if divisor == 0 {
613 65_536
614 } else {
615 u32::from(divisor)
616 };
617 speaker.state.epoch = self.event_time;
618 speaker.state.mode = 3;
619 speaker.state.gate = enabled;
620 speaker.state.data = enabled;
621 }
622 }
623 MusicEvent::Midi(message) => {
624 #[cfg(feature = "mpu401")]
625 if let Some(midi) = &mut self.midi {
626 midi.message(message.bytes());
627 }
628 #[cfg(not(feature = "mpu401"))]
629 let _ = message;
630 }
631 MusicEvent::SysEx { offset, length } => {
632 #[cfg(feature = "mpu401")]
633 if let (Some(midi), Some(player)) = (&mut self.midi, &self.player) {
634 midi.message(
635 &player.sequence.sysex[offset as usize..offset as usize + length as usize],
636 );
637 }
638 #[cfg(not(feature = "mpu401"))]
639 let _ = (offset, length);
640 }
641 MusicEvent::Psg(byte) => {
642 #[cfg(feature = "psg")]
643 if let Some(psg) = &mut self.psg {
644 psg.write(byte);
645 }
646 #[cfg(not(feature = "psg"))]
647 let _ = byte;
648 }
649
650 MusicEvent::Opl { register, value } => {
651 #[cfg(feature = "opl")]
652 self.write_opl(register, value);
653 #[cfg(not(feature = "opl"))]
654 let _ = (register, value);
655 }
656 }
657 }
658
659 #[cfg(feature = "opl")]
660 fn write_opl(&mut self, register: u16, value: u8) {
661 if let Some(opl) = &mut self.opl {
662 if let Some(capture) = &mut self.capture {
663 capture.record(self.event_time, register, value);
664 }
665 opl.write(register, value);
666 }
667 }
668
669 fn release_notes(&mut self) {
670 if self
671 .player
672 .as_ref()
673 .is_some_and(|p| p.sequence.target == MusicTarget::Native)
674 {
675 return;
676 }
677
678 if self
679 .player
680 .as_ref()
681 .is_some_and(|player| player.sequence.target == MusicTarget::Midi)
682 {
683 #[cfg(feature = "mpu401")]
684 if let Some(midi) = &mut self.midi {
685 midi.release();
686 }
687 return;
688 }
689 if self.player.as_ref().is_some_and(|player| {
690 matches!(
691 player.sequence.target,
692 MusicTarget::Psg | MusicTarget::OplPsg(_)
693 )
694 }) {
695 #[cfg(feature = "psg")]
696 if let Some(psg) = &mut self.psg {
697 psg.mute();
698 }
699 if self
700 .player
701 .as_ref()
702 .is_some_and(|player| player.sequence.target == MusicTarget::Psg)
703 {
704 return;
705 }
706 }
707
708 if self
709 .player
710 .as_ref()
711 .is_some_and(|player| player.sequence.target == MusicTarget::Speaker)
712 {
713 if let Some(speaker) = &mut self.speaker {
714 speaker.state.data = false;
715 }
716 } else {
717 #[cfg(feature = "opl")]
718 if let Some(opl) = &mut self.opl {
719 let capture = &mut self.capture;
720 let at = self.event_time;
721 opl.release_notes(|register, value| {
722 if let Some(capture) = capture {
723 capture.record(at, register, value);
724 }
725 });
726 }
727 }
728 }
729
730 pub fn render(&mut self, out: &mut [Frame]) {
737 let denominator = self.denominator();
738 for frame in out {
739 let start = u128::from(self.stats.frames) * u128::from(Tick::HZ_NUMERATOR);
740 let end = start + u128::from(Tick::HZ_NUMERATOR);
741 self.event_time = Tick((start / denominator) as u64);
742 if let Some(capture) = &mut self.capture {
743 capture.boundary(self.event_time);
744 }
745 if let Some(player) = &mut self.player
746 && player.prepare(Tick((start / denominator) as u64))
747 {
748 self.release_notes();
749 }
750 if let Some(player) = &mut self.speaker_player
751 && player.prepare(self.event_time)
752 && let Some(speaker) = &mut self.speaker
753 {
754 speaker.state.data = false;
755 }
756 self.switch_music_device();
757 let mut speaker_gain = self.speaker_player.as_mut().map_or(1.0, Player::gain);
758 let mut gain = self.player.as_mut().map_or(1.0, Player::gain);
759 let mut cursor = start;
760 let mut speaker_area = 0.0;
761 #[cfg(any(feature = "mpu401", feature = "psg"))]
762 let mut extra_area = [0.0f64; 2];
763 #[cfg(not(any(feature = "mpu401", feature = "psg")))]
764 let extra_area = [0.0f64; 2];
765 #[cfg(feature = "sb")]
766 let mut dac_area = [0.0; 2];
767 #[cfg(feature = "opl")]
768 let mut opl_area = [0.0f64; 2];
769 #[cfg(not(feature = "opl"))]
770 let opl_area = [0.0f64; 2];
771 while cursor < end {
772 self.event_time = Tick((cursor / denominator) as u64);
773 if self.runtime_due(Tick((cursor / denominator) as u64)) {
774 gain = self.player.as_mut().map_or(1.0, Player::gain);
775 }
776 if self.speaker_due(self.event_time) {
777 speaker_gain = self.speaker_player.as_mut().map_or(1.0, Player::gain);
778 }
779 while let Ok(command) = self.commands.peek() {
780 let stamp = u128::from(self.command_time(command).0) * denominator;
781 if stamp > cursor {
782 break;
783 }
784 if stamp < cursor {
785 self.stats.late_commands += 1;
786 }
787 let command = self.commands.pop().expect("peeked command");
788 self.apply(command.command);
789 }
790 while self
791 .player
792 .as_ref()
793 .and_then(Player::next)
794 .is_some_and(|at| u128::from(at.0) * denominator <= cursor)
795 {
796 let player = self.player.as_mut().expect("scheduled player");
797 let event = player.pop();
798 let ended = player.ended;
799 if let Some(event) = event {
800 self.music_event(event);
801 }
802 if ended {
803 self.release_notes();
804 }
805 }
806 #[cfg(feature = "opl")]
807 if let Some(opl) = &mut self.opl
808 && u128::from(opl.next.0) * denominator <= cursor
809 {
810 opl.step();
811 }
812 #[cfg(feature = "mpu401")]
813 if let Some(midi) = &mut self.midi
814 && u128::from(midi.next.0) * denominator <= cursor
815 {
816 midi.step();
817 }
818 #[cfg(feature = "psg")]
819 if let Some(psg) = &mut self.psg
820 && u128::from(psg.next.0) * denominator <= cursor
821 {
822 psg.step();
823 }
824 let mut next = end;
825 if let Some(at) = self.speaker_next(self.event_time) {
826 next = next.min(u128::from(at.0) * denominator);
827 }
828 if let Some(at) = self.runtime_next(Tick((cursor / denominator) as u64)) {
829 next = next.min(u128::from(at.0) * denominator);
830 }
831 if let Ok(command) = self.commands.peek() {
832 next = next.min(u128::from(self.command_time(command).0) * denominator);
833 }
834 if let Some(at) = self.player.as_ref().and_then(Player::next) {
835 next = next.min(u128::from(at.0) * denominator);
836 }
837 #[cfg(feature = "opl")]
838 if let Some(opl) = &self.opl {
839 next = next.min(u128::from(opl.next.0) * denominator);
840 }
841 #[cfg(feature = "mpu401")]
842 if let Some(midi) = &self.midi {
843 next = next.min(u128::from(midi.next.0) * denominator);
844 }
845 #[cfg(feature = "psg")]
846 if let Some(psg) = &self.psg {
847 next = next.min(u128::from(psg.next.0) * denominator);
848 }
849 if next <= cursor {
851 continue;
852 }
853 let from = cursor as f64 / denominator as f64;
854 let to = next as f64 / denominator as f64;
855 if let Some(speaker) = &self.speaker {
856 speaker_area += speaker.state.integral(from, to);
857 }
858 #[cfg(feature = "sb")]
859 if let Some(dac) = self.dac {
860 for (area, sample) in dac_area.iter_mut().zip(dac.output()) {
861 *area += sample * (to - from);
862 }
863 }
864 #[cfg(feature = "opl")]
865 if let Some(opl) = &self.opl {
866 for (channel, (area, value)) in opl_area.iter_mut().zip(opl.held).enumerate() {
867 #[cfg(feature = "sb")]
868 let value = f64::from(value)
869 * f64::from(self.dac.map_or(1.0, |dac| dac.fm_gain[channel]));
870 #[cfg(not(feature = "sb"))]
871 let value = {
872 let _ = channel;
873 f64::from(value)
874 };
875 let value = if self.soundtrack_mutes(MusicTarget::Opl(opl.chip)) {
876 0.0
877 } else {
878 value
879 };
880 *area += value
881 * (to - from)
882 * if self.runtime.is_some()
883 && self.player.as_ref().is_none_or(|p| {
884 matches!(
885 p.sequence.target,
886 MusicTarget::Opl(_) | MusicTarget::OplPsg(_)
887 )
888 })
889 {
890 gain
891 } else {
892 1.0
893 };
894 }
895 }
896 #[cfg(feature = "mpu401")]
897 if let Some(midi) = &self.midi {
898 let gain = if self
899 .player
900 .as_ref()
901 .is_some_and(|p| p.sequence.target == MusicTarget::Midi)
902 {
903 gain
904 } else {
905 1.0
906 };
907 for (area, sample) in extra_area.iter_mut().zip(midi.held) {
908 *area += f64::from(sample)
909 * (to - from)
910 * gain
911 * if self.soundtrack_mutes(MusicTarget::Midi) {
912 0.0
913 } else {
914 1.0
915 };
916 }
917 }
918 #[cfg(feature = "psg")]
919 if let Some(psg) = &self.psg {
920 let gain = if self.player.as_ref().is_some_and(|p| {
921 matches!(p.sequence.target, MusicTarget::Psg | MusicTarget::OplPsg(_))
922 }) {
923 gain
924 } else {
925 1.0
926 };
927 for (area, sample) in extra_area.iter_mut().zip(psg.held) {
928 *area += f64::from(sample)
929 * (to - from)
930 * gain
931 * if self.soundtrack_mutes(MusicTarget::Psg) {
932 0.0
933 } else {
934 1.0
935 };
936 }
937 }
938 cursor = next;
939 }
940 let width = Tick::HZ_NUMERATOR as f64 / denominator as f64;
941 let speaker = self
942 .speaker
943 .as_mut()
944 .map_or(0.0, |speaker| speaker.filter(speaker_area / width));
945 let speaker_gain = if self.speaker_player.is_some() {
946 speaker_gain
947 } else if self
948 .player
949 .as_ref()
950 .is_some_and(|p| p.sequence.target == MusicTarget::Speaker)
951 {
952 gain
953 } else {
954 1.0
955 };
956 let speaker = speaker
957 * speaker_gain as f32
958 * if self.speaker_player.is_none() && self.soundtrack_mutes(MusicTarget::Speaker) {
959 0.0
960 } else {
961 1.0
962 };
963 let native = self
964 .native_sample(start, denominator)
965 .map(|s| s * gain as f32);
966 #[cfg(feature = "sb")]
967 let pcm = core::array::from_fn::<_, 2, _>(|channel| {
968 let mean = dac_area[channel] / width;
969 let hz = self.dac.map_or(0, |dac| dac.filter_hz);
970 if hz != 0 {
971 let alpha =
972 1.0 - (-std::f64::consts::TAU * f64::from(hz) / f64::from(self.rate)).exp();
973 self.dac_filter[channel] += alpha * (mean - self.dac_filter[channel]);
974 self.dac_filter[channel] as f32
975 } else {
976 self.dac_filter[channel] = mean;
977 mean as f32
978 }
979 });
980 #[cfg(not(feature = "sb"))]
981 let pcm = [0.0; 2];
982 let gain = if self.runtime.is_some()
983 || self.player.as_ref().is_some_and(|p| {
984 !matches!(
985 p.sequence.target,
986 MusicTarget::Opl(_) | MusicTarget::OplPsg(_)
987 )
988 }) {
989 1.0
990 } else {
991 gain
992 };
993 *frame = [
994 (speaker
995 + pcm[0]
996 + (extra_area[0] / width) as f32
997 + native[0]
998 + (opl_area[0] / width * gain) as f32)
999 .clamp(-1.0, 1.0),
1000 (speaker
1001 + pcm[1]
1002 + (extra_area[1] / width) as f32
1003 + native[1]
1004 + (opl_area[1] / width * gain) as f32)
1005 .clamp(-1.0, 1.0),
1006 ];
1007 self.stats.frames += 1;
1008 if let Some(runtime) = &self.runtime {
1009 runtime.publish_time(self.now());
1010 }
1011 if let Some(runtime) = &self.speaker_runtime {
1012 runtime.publish_time(self.now());
1013 }
1014 }
1015 }
1016}
1017
1018#[cfg(feature = "toolkit")]
1019impl game_toolkit_audio::StereoStream for AudioEngine {
1020 fn fill(&mut self, out: &mut [[f32; 2]]) {
1021 self.render(out);
1022 }
1023}