Suggestion
Would it be possible to add <name>Callback::new functions that take ownership of a callback implementation, rather than borrowing it? Eg:
impl IXAudio2VoiceCallback {
// Now T is boxed and owned by this wrapper
pub fn new_owned<T: IXAudio2VoiceCallback_Impl>(this: T) -> windows_core::NonScopedInterface<Self> { }
}
Specifically, IXAudio2SourceVoice callbacks MUST outlive the source voice. IXAudio2SourceVoice::Start is asynchronous, and returns before playback has finished.
And the only way to figure out if the sound is finished is:
- either by periodically fetching the state (
IXAudio2SourceVoice::GetState)
- or by getting a notification via
IXAudio2VoiceCallback::OnBufferEnd callback, from which we would normally either destroy or reuse that voice.
And the following is UB:
let callb = MyCallback;
let i_callb: ScopedInterface<'_, IXAudio2VoiceCallback> = IXAudio2VoiceCallback::new(&callb);
let mut source: Option<IXAudio2SourceVoice> = None;
ixaudio2.CreateSourceVoice(&mut source, pcallback: Some(i_callb.deref()), ...);
source.SubmitSourceBuffer(...)
source.Start(...)
return;
because we return while the source voice is still alive and expects the pointer to the vtable to be valid.
So the proper usage would be to instantiate the callback once at the very beginning, store and use it for each source voice creation:
struct XAudio2 {
ixaudio2: IXAudio2,
callback: NonScopedInterface<IXAudio2VoiceCallback>,
}
impl XAudio2 {
pub fn new() -> Self {
// create IXAudio2, mastering voice, etc ...
let callback = MyCallback;
Self { ixaudio2, callback: IXAudio2VoiceCallback::new_owned(callback) }
}
pub fn play_sound(&self, path: ...) {
let mut source: Option<IXAudio2SourceVoice> = None;
self.ixaudio2.CreateSourceVoice(&mut source, ..., pcallback: Some(self.callback.deref()), ...);
source.SubmitSourceBuffer(...);
source.Start(...);
return;
}
}
With the current approach, storing both MyCallback and the ScopedInterface (which is tied to the callback's lifetime) inside a single struct would be a difficult challenge.
Suggestion
Would it be possible to add
<name>Callback::newfunctions that take ownership of a callback implementation, rather than borrowing it? Eg:Specifically,
IXAudio2SourceVoicecallbacks MUST outlive the source voice.IXAudio2SourceVoice::Startis asynchronous, and returns before playback has finished.And the only way to figure out if the sound is finished is:
IXAudio2SourceVoice::GetState)IXAudio2VoiceCallback::OnBufferEndcallback, from which we would normally either destroy or reuse that voice.And the following is UB:
because we return while the source voice is still alive and expects the pointer to the vtable to be valid.
So the proper usage would be to instantiate the callback once at the very beginning, store and use it for each source voice creation:
With the current approach, storing both
MyCallbackand theScopedInterface(which is tied to the callback's lifetime) inside a single struct would be a difficult challenge.