rawdio/effects/recorder/
recorder_node.rs

1use super::{recorder_event::*, recorder_notification::*, recorder_processor::RecorderProcessor};
2use crate::{
3    commands::Id, effects::Channel, engine::NotifierStatus, graph::DspParameters, prelude::*,
4};
5use std::{cell::RefCell, rc::Rc};
6
7/// A node that records its input
8///
9/// The recorder node doesn't produce output so should be at the end of the
10/// chain
11///
12/// Call `take_recording()` to get the recording from the node
13pub struct Recorder {
14    /// The node to connect to the audio graph
15    pub node: GraphNode,
16    event_transmitter: RecorderEventTransmitter,
17    notification_receiver: RecorderNotificationReceiver,
18    current_recording: Option<OwnedAudioBuffer>,
19    is_recording: bool,
20}
21
22static EVENT_CHANNEL_CAPACITY: usize = 32;
23static NOTIFICATION_CHANNEL_CAPACITY: usize = 32;
24
25impl Recorder {
26    /// Create a new recorder node
27    pub fn new(
28        context: &mut dyn Context,
29        channel_count: usize,
30        sample_rate: usize,
31    ) -> Rc<RefCell<Self>> {
32        let id = Id::generate();
33
34        let (event_transmitter, event_receiver) = Channel::bounded(EVENT_CHANNEL_CAPACITY);
35        let (notification_transmitter, notification_receiver) =
36            Channel::bounded(NOTIFICATION_CHANNEL_CAPACITY);
37
38        let processor = Box::new(RecorderProcessor::new(
39            sample_rate,
40            channel_count,
41            event_receiver,
42            notification_transmitter,
43        ));
44
45        let output_count = 0;
46
47        let node = GraphNode::new(
48            id,
49            context,
50            channel_count,
51            output_count,
52            processor,
53            DspParameters::empty(),
54        );
55
56        let recorder = Rc::new(RefCell::new(Self {
57            node,
58            event_transmitter,
59            notification_receiver,
60            current_recording: None,
61            is_recording: false,
62        }));
63
64        let weak_recorder = Rc::downgrade(&recorder);
65
66        context.add_notifier(Box::new(move || {
67            if let Some(recorder) = weak_recorder.upgrade() {
68                recorder.borrow_mut().process_notifications();
69                return NotifierStatus::Continue;
70            }
71
72            NotifierStatus::Remove
73        }));
74
75        recorder
76    }
77
78    /// Start recording
79    pub fn record_now(&mut self) {
80        let _ = self.event_transmitter.send(RecorderEvent::start_now());
81    }
82
83    /// Stop recording
84    pub fn stop_record_now(&mut self) {
85        let _ = self.event_transmitter.send(RecorderEvent::stop_now());
86    }
87
88    /// Stop recording at a particular time
89    pub fn stop_record_at_time(&mut self, time: Timestamp) {
90        let _ = self
91            .event_transmitter
92            .send(RecorderEvent::stop_at_time(time));
93    }
94
95    /// Query if the recorder is currently recording
96    pub fn is_recording(&self) -> bool {
97        self.is_recording
98    }
99
100    /// Take the current recording from the node
101    ///
102    /// This will clear the recording
103    pub fn take_recording(&mut self) -> Option<OwnedAudioBuffer> {
104        self.current_recording.take()
105    }
106
107    fn append_buffer(&mut self, buffer: &OwnedAudioBuffer, samples_used: usize) {
108        let slice = BorrowedAudioBuffer::slice_frames(buffer, 0, samples_used);
109        self.current_recording = match &self.current_recording {
110            Some(current_recording) => Some(current_recording.extended_with_buffer(&slice)),
111            None => Some(OwnedAudioBuffer::from_buffer(&slice)),
112        };
113    }
114
115    fn return_buffer(&mut self, buffer: OwnedAudioBuffer) {
116        let _ = self
117            .event_transmitter
118            .send(RecorderEvent::return_buffer(buffer));
119    }
120
121    fn process_notifications(&mut self) {
122        while let Ok(event) = self.notification_receiver.try_recv() {
123            match event {
124                RecorderNotification::Start => self.is_recording = true,
125                RecorderNotification::Data(buffer, samples_used) => {
126                    self.append_buffer(&buffer, samples_used);
127                    self.return_buffer(buffer);
128                }
129                RecorderNotification::Stop => self.is_recording = false,
130            }
131        }
132    }
133}