voicemeeter\interface\callback/
register.rs

1//! Functions for callbacks in Voicemeeter.
2use std::{
3    ffi::{CString, NulError},
4    os::raw::c_long,
5    ptr,
6};
7
8use crate::{
9    bindings::VBVMR_CBCOMMAND, interface::callback::data::RawCallbackData, CallbackCommand,
10    VoicemeeterRemote,
11};
12
13/***************************************************************************** */
14/* VB-AUDIO CALLBACK */
15/***************************************************************************** */
16/* 4x Functions to process all voicemeeter audio input and output channels */
17/*  */
18/* VBVMR_AudioCallbackRegister	 :to register your audio callback(s) */
19/* VBVMR_AudioCallbackStart	     :to start the audio stream */
20/* VBVMR_AudioCallbackStop    	 :to stop the audio stream */
21/* VBVMR_AudioCallbackUnregister :to unregister / Release callback(s) */
22/***************************************************************************** */
23
24// Thanks to sgrif, http://blog.sagetheprogrammer.com/neat-rust-tricks-passing-rust-closures-to-c
25fn register_audio_callback<'cb, F>(
26    remote: &VoicemeeterRemote,
27    mode: &crate::AudioCallbackMode,
28    application: *mut std::os::raw::c_char,
29    callback: F,
30) -> Result<*mut F, AudioCallbackRegisterError>
31where
32    F: FnMut(CallbackCommand<'cb>, i32) -> c_long,
33{
34    // This leaks
35    let data = Box::into_raw(Box::new(callback));
36    tracing::debug!("callback {:p}", data);
37    let res = unsafe {
38        remote.raw.VBVMR_AudioCallbackRegister(
39            mode.0,
40            Some(call_closure::<F>),
41            data as *mut _,
42            application,
43        )
44    };
45    tracing::debug!("registered application");
46    match res {
47        0 => Ok(data),
48        -1 => Err(AudioCallbackRegisterError::NoServer),
49        1 => Err(AudioCallbackRegisterError::AlreadyRegistered(unsafe {
50            CString::from_raw(application)
51        })),
52        s => Err(AudioCallbackRegisterError::Unexpected(s)),
53    }
54}
55
56unsafe extern "C" fn call_closure<'cb, F>(
57    user_data: *mut std::os::raw::c_void,
58    command: c_long,
59    buffer: *mut std::os::raw::c_void,
60    nnn: c_long,
61) -> c_long
62where
63    F: FnMut(CallbackCommand<'cb>, i32) -> c_long,
64{
65    let callback_ptr = user_data as *mut F;
66    let callback = unsafe { &mut *callback_ptr };
67    let ptr = RawCallbackData::from_ptr(buffer);
68    callback(
69        unsafe {
70            CallbackCommand::new_unchecked(
71                crate::types::VoicemeeterApplication::PotatoX64Bits,
72                VBVMR_CBCOMMAND(command),
73                ptr,
74            )
75        },
76        nnn,
77    )
78}
79
80/// Guard type for the callback. If this is dropped the callback data will be leaked and newer dropped.
81#[must_use = "This structure contains the raw pointer to the closure environment, if this is not returned you will leak memory"]
82pub struct CallbackGuard<'a, F> {
83    guard: *mut F,
84    lt: std::marker::PhantomData<&'a ()>,
85}
86
87impl VoicemeeterRemote {
88    /// Register a callback for audio.
89    ///
90    /// The callback is a function that will be called when the audio stream is started, changed or stopped and when data is sent to it.
91    ///
92    /// The callback takes two arguments, the command sent from voicemeeter and a currently unused [i32] parameter for additional data.
93    ///
94    /// The [mode](crate::AudioCallbackMode) determines what buffers are returned.
95    ///
96    /// # Examples
97    ///
98    /// ```rust,no_run
99    #[doc = include_str!("../../../examples/simple.rs")]
100    /// ```
101    /// 
102    /// ## Complete example
103    /// ```rust,no_run
104    #[doc = include_str!("../../../examples/output.rs")]
105    /// ```
106    ///
107    #[tracing::instrument(skip(application_name, callback), fields(application_name, mode))]
108    pub fn audio_callback_register<'a, 'cb, 'g, F>(
109        &'a self,
110        mode: crate::AudioCallbackMode,
111        application_name: impl AsRef<str>,
112        callback: F,
113    ) -> Result<CallbackGuard<'g, F>, AudioCallbackRegisterError>
114    where
115        F: FnMut(CallbackCommand<'cb>, i32) -> c_long + 'g,
116    {
117        let application_name = application_name.as_ref();
118        tracing::Span::current().record("application_name", application_name);
119        //let ctx_span = tracing::trace_span!("voicemeeter_callback");
120        //ctx_span.record("application_name", &application_name).record("mode", &mode.0).follows_from(tracing::Span::current());
121        assert!(application_name.len() < 64);
122        let mut application = [b'\0'; 64];
123        application[0..application_name.len()].copy_from_slice(application_name.as_bytes());
124        let ptr = ptr::addr_of!(self.program);
125        tracing::info!("a: {ptr:p}");
126
127        let g = register_audio_callback(
128            self,
129            &mode,
130            ptr::addr_of_mut!(application) as *mut _,
131            callback,
132        )?;
133
134        Ok(CallbackGuard {
135            guard: g,
136            lt: Default::default(),
137        })
138    }
139
140    /// Unregister a callback. This implicitly calls [`VoicemeeterRemote::audio_callback_stop`].
141    pub fn audio_callback_unregister<F>(
142        &self,
143        guard: CallbackGuard<'_, F>,
144    ) -> Result<(), AudioCallbackUnregisterError> {
145        let res = unsafe { self.raw.VBVMR_AudioCallbackUnregister() };
146        match res {
147            0 => {
148                let _ = unsafe { Box::from_raw(guard.guard) };
149                Ok(())
150            }
151            -1 => Err(AudioCallbackUnregisterError::NoServer),
152            1 => Err(AudioCallbackUnregisterError::AlreadyUnregistered),
153            s => Err(AudioCallbackUnregisterError::Unexpected(s)),
154        }
155    }
156
157    /// Unregister a callback without dropping the callback, thus leaking data. This implicitly calls [`VoicemeeterRemote::audio_callback_stop`].
158    pub fn audio_callback_unregister_leak<F>(&self) -> Result<(), AudioCallbackUnregisterError> {
159        let res = unsafe { self.raw.VBVMR_AudioCallbackUnregister() };
160        match res {
161            0 => Ok(()),
162            -1 => Err(AudioCallbackUnregisterError::NoServer),
163            1 => Err(AudioCallbackUnregisterError::AlreadyUnregistered),
164            s => Err(AudioCallbackUnregisterError::Unexpected(s)),
165        }
166    }
167}
168
169/// Errors that can occur while registering an audio callback.
170#[derive(Debug, Clone, thiserror::Error)]
171#[non_exhaustive]
172pub enum AudioCallbackRegisterError {
173    // TODO: is this correct?
174    /// No server.
175    #[error("no server")]
176    NoServer,
177    /// Application is already registered.
178    #[error("an application `{}` is already registered", _0.to_string_lossy())]
179    AlreadyRegistered(CString),
180    /// Could not make a c-string. This is a bug.
181    #[error("could not make application name into a c-string")]
182    NulError(#[from] NulError),
183    /// An unknown error code occured.
184    #[error("unexpected error occurred: error code {0}")]
185    Unexpected(i32),
186}
187
188/// Errors that can occur while unregistering the audio callback.
189#[derive(Debug, Clone, thiserror::Error)]
190#[non_exhaustive]
191pub enum AudioCallbackUnregisterError {
192    /// No server.
193    #[error("no server")]
194    NoServer,
195    /// Application is already unregistered.
196    #[error("callback already unregistered")]
197    AlreadyUnregistered,
198    /// An unknown error code occured.
199    #[error("an unexpected error occurred: error code {0}")]
200    Unexpected(i32),
201}