summaryrefslogtreecommitdiff
path: root/src/audio.rs
blob: 15522923a6fc2ae8af26682035a216ffa4f5d2a2 (plain)
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
//! Contains all types and implementations related to audio processing and the audio thread.

use crate::{
    RetainPluginMainThread, RetainPluginShared,
    params::{GestureChange, RetainParamsLocal, RetainParamsShared},
};
use clack_extensions::{audio_ports::*, params::PluginAudioProcessorParams};
use clack_plugin::{
    events::event_types::{ParamGestureBeginEvent, ParamGestureEndEvent},
    prelude::*,
};

/// Our plugin's audio processor. It lives in the audio thread.
///
/// It receives parameter events, and process a stereo audio signal by operating on the given audio
/// buffer.
pub struct RetainPluginAudioProcessor<'a> {
    /// The local state of the parameters
    params: RetainParamsLocal,
    /// A reference to the plugin's shared data.
    shared: &'a RetainPluginShared,
    /// Our handle to the host
    host: HostAudioProcessorHandle<'a>,
}

impl<'a> PluginAudioProcessor<'a, RetainPluginShared, RetainPluginMainThread<'a>>
    for RetainPluginAudioProcessor<'a>
{
    fn activate(
        host: HostAudioProcessorHandle<'a>,
        _main_thread: &mut RetainPluginMainThread,
        shared: &'a RetainPluginShared,
        _audio_config: PluginAudioConfiguration,
    ) -> Result<Self, PluginError> {
        // This is where we would allocate intermediate buffers and such if we needed them.
        Ok(Self {
            host,
            shared,
            params: RetainParamsLocal::new(&shared.params),
        })
    }

    fn process(
        &mut self,
        _process: Process,
        mut audio: Audio,
        events: Events,
    ) -> Result<ProcessStatus, PluginError> {
        // First, we have to make a few sanity checks.
        // We want at least a single input/output port pair, which contains channels of `f32`
        // audio sample data.
        let mut port_pair = audio
            .port_pair(0)
            .ok_or(PluginError::Message("No input/output ports found"))?;

        let mut output_channels = port_pair
            .channels()?
            .into_f32()
            .ok_or(PluginError::Message("Expected f32 input/output"))?;

        let mut channel_buffers = [None, None];

        // Extract the buffer slices that we need, while making sure they are paired correctly and
        // check for either in-place or separate buffers.
        for (pair, buf) in output_channels.iter_mut().zip(&mut channel_buffers) {
            *buf = match pair {
                ChannelPair::InputOnly(_) | ChannelPair::OutputOnly(_) => None,
                ChannelPair::InPlace(b) => Some(b),
                ChannelPair::InputOutput(i, o) => {
                    o.copy_from_slice(i);
                    Some(o)
                }
            }
        }

        // Receive any param updates from the main thread and/or the GUI.
        let has_ui_param_updates = self.params.fetch_updates(&self.shared.params);

        // Now let's process the audio, while splitting the processing in batches between each
        // sample-accurate event.

        for event_batch in events.input.batch() {
            // Process all param events in this batch
            for event in event_batch.events() {
                self.params.handle_event(event);
            }

            // Get the order value after all parameter changes have been handled.
            let order = self.params.get_order();

            for buf in channel_buffers.iter_mut().flatten() {
                for sample in buf.iter_mut() {
                    *sample = order as f32;
                }
            }
        }

        // Publish any parameter changes we may have received back to the GUI.
        if self.params.push_updates(&self.shared.params) {
            // Request the on-main-thread callback, which we use to refresh the UI if it is open
            self.host.request_callback();
        }

        // Fetch the latest gesture status
        let current_gesture = self
            .params
            .fetch_gesture(&self.shared.params, has_ui_param_updates);

        // Send a Gesture Begin event, if we need to do so
        if let Some(GestureChange::Begin | GestureChange::Both) = current_gesture {
            let _ = events.output.try_push(ParamGestureBeginEvent::new(
                0,
                RetainParamsShared::PARAM_ORDER_ID,
            ));
        }

        // If the UI sent us param updates, send them to the Host
        if has_ui_param_updates {
            self.params.send_param_events(events.output);
        }

        // Send a Gesture End event, if we need to do so
        if let Some(GestureChange::End | GestureChange::Both) = current_gesture {
            let _ = events.output.try_push(ParamGestureEndEvent::new(
                audio.frames_count(),
                RetainParamsShared::PARAM_ORDER_ID,
            ));
        }

        Ok(ProcessStatus::ContinueIfNotQuiet)
    }
}

impl PluginAudioPortsImpl for RetainPluginMainThread<'_> {
    fn count(&mut self, _is_input: bool) -> u32 {
        1
    }

    fn get(&mut self, index: u32, _is_input: bool, writer: &mut AudioPortInfoWriter) {
        if index == 0 {
            writer.set(&AudioPortInfo {
                id: ClapId::new(0),
                name: b"main",
                channel_count: 2,
                flags: AudioPortFlags::IS_MAIN,
                port_type: Some(AudioPortType::STEREO),
                in_place_pair: None,
            });
        }
    }
}

impl PluginAudioProcessorParams for RetainPluginAudioProcessor<'_> {
    fn flush(
        &mut self,
        input_parameter_changes: &InputEvents,
        _output_parameter_changes: &mut OutputEvents,
    ) {
        for event in input_parameter_changes {
            self.params.handle_event(event);
        }
    }
}