-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ASIO support to CPAL #52
Comments
cc @freesig here's a very rough outline of how I'd approach this. Feel free to post comments or call if you get started on this before we hang and anything is unclear of if you get stuck 👍 Setup
Generate Rust BindingsUse rust-bindgen to generate a module of rust bindings to all items/function is ASIO from the ASIO header file(s). The user guide is super handy. There are two options for approaches here - 1. dynamically generate bindings from header at build time using a build.rs script or 2. generate bindings ahead of time using the bindgen CLI. The rust-bindgen folks recommend approach 1. so I reckon we try that. This will mean adding: Keep in mind that users should still be able to use cpal on windows without installing ASIO, so we'll have to make sure to fail "gracefully"/silently if ASIO is not available and provide a runtime check for checking whether or not it is available. CPAL API ImplementationWe can now begin implementing the API in CPAL. Tomaka's preference for this seems to be to have an operating-system specific extensions module for each module which will contain all items uniquely availble on that OS. As there are currently none of these modules yet in cpal, we will add them. First, we'll add a Currently, CPAL is setup so that windows defaults directly to using the extern crate cpal;
fn main() {
// Only generate code for this block if we're targeting windows.
#[cfg(target_os = "windows")]
{
if cpal::os::windows::is_asio_supported() {
cpal::os::windows::initialise_asio().unwrap();
}
}
// All following cpal function calls and items will now use the `asio` module internally now rather than `wasapi`.
let event_loop = cpal::EventLoop::new();
} So internally, I imagine the windows backend event loop looking something like this: pub enum EventLoop {
Asio(asio::EventLoop),
Wasapi(wasapi::EventLoop),
} The CPAL provides a |
Dante Virtual Soundcard is complaining about the license being activated too many times. I can get a trial for developing. Might need @JoshuaBatty to login to the Audinate website and ask them for a to reset it because we need to move it to the NUC. Or just send me the Audinate login / pass and I can sort it. Also a little update on this. I've finally (with lots of help from #rust irc) got cpal to compile and link with the ASIO SDK. I'm about to test the if I can see any asio drivers. |
Ok I can see the Dante asio driver from the asio-sys test :) |
Ok I've got it showing 64 channels. |
Get audio both directions through ASIO.Use CPAL examples beep and record to test this works. Please feel free to add / edit these if my understanding is off:
Plan to get this done in a little 2 - 4 day sprint starting tomorrow. |
Nice one @freesig, sounds great, I'll be working on audio_server non-stop until its done starting tomorrow - feel free to hit me up if you'd like help/advice at any point! Maybe we can do a voice at some point and make a game plan for now until completion 👍 |
Hey @mitchmindtree today I tried to fill out a Format struct from ASIO.
It's complaining that it can't link: TomorrowWe should definitely have a voice chat tomorrow afternoon (my time, your morning I think) because I'm not that sure on how to handle this os::windows mod etc. |
Ok got passed the link error. |
Ok so now:
Question |
Nice work!!
The safest way to do ths would be to use the lazy_static crate - it allows you to lazily initialize statics. Btw, the reason why mutatinng a Edit: Actually the |
Yeh I was thinking some sort of lazy init would be what we needed and that lib looks like what we need |
Ok really basic version of enumerate is working for asio now. |
Ok so I've hit a bit of a wall with the "Running" part. Situation
ProblemHow can I communicate from this callback to anything in in cpal. Things I've tried
I'm thinking of going the global variable option but I just wanted some advice before going down that road. |
By this, do you mean that this callback is the function in which the user is expected to fill the next buffer? If not, what exactly is the user expected to do when this function is called? Just want to clarify this before adding further advice |
Yeh exactly. So the doubleBufferIndex is either 0 or 1 and that is set to the index of the buffer you should fill now. |
OK sweet, yeah I think the best bet for this would be to use a closure and do something similar to what is going on in the coreaudio backend. It may be necessary to |
I think I'm getting close to getting example beep to run on asio but I'm stuck on one last problem:
Problem: Box needs to know the lifetime of the closure to make sure the captured data lives long enough. In the coreaudio version they got around in by doing this:
Which stores into this: Seems very unsafe to transmute the callback. |
Send and Sync traitsSo, the
The std docs should also have a lot of info on these. Boxed Closure LifetimeSo the main reason the box needs to know about the lifetime of the closure is because it is being sent from the current thread to the audio thread. This means the lifetime of the given callback must be static as there is no other way to guarantee that the lifetime of the callback is at least as long as its use on the audio thread. In this case, the Will talk to ya some more in person soon 👍 |
UpdateWorking:The beep example is now working smoothly. It sounds correct. Issues:
My interpretation is that you get a buffer and then that is for say channel 1. Then it swaps and the next buffer is for channel 2. Then it swaps and the next buffer is for channel 3.
SummaryTheres probably a reason that portaudio-asio is 3000 lines but I found this great file outlining how they did it HERE |
actually on second thought it looks like the buffer pointers are per channel. Which is odd because I'm only using one but I'm getting stereo. This might just be luck because they are next to each other in memory. Gotta love unsafe land |
As a fallback, I’ll have a Mac mini on standby.
Out of curiosity, I attempted to compile on a Linux machine (up-to date
archlinux) and got a segfault from pulseaudio.
This is obviously in no way something you or Mitch need to look into, but
something interesting to look out for in the future.
…On Fri, 20 Apr 2018 at 00:42, Tom ***@***.***> wrote:
actually on second thought it's possible that each buffer contains enough
memory for all the channels
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#52 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AIPI7RFH9MDgBCwltDdA9koBPko48jVzks5tqKI6gaJpZM4SGVwT>
.
|
@BlueJayLouche I think the main issue on linux is that by default PulseAudio takes exclusive access to the audio hardware and CPAL (the cross platform audio backend we're using) currently only targets ALSA directly. As a result if pulseaudio is running, you eventually run into some contention or a data race which can cause a segfault or some other weird undefined behaviour. I think there are a couple workarounds for this:
Just thought I'd add this for the record in case someone comes across this in the future! @freesig Awesome work on getting the CPAL ASIO beep going and finding that portaudio reference doc, looks super useful! De-interleaving This is isn't too uncommon to have to interleave or deinterleave buffers at different parts of the signal chain, and will rarely show up as a bottleneck (especially if we only have to do it once ASIO -> CPAL or vice versa). My understanding is that non-interleaved channels can be more efficient for some purposes and interleaved can be more efficient for others. It would definitely be useful if CPAL actually just served the original buffers whether they are interleaved or not, maybe in some enum. Endianness We can possibly use the |
For some reason Dante produces a device for every two channels on Windows' WDM API. This is an issue as the audio server expects to be able to target a single device - targeting multiple devices would require a significant hack and be a special condition only required on windows.
The easiest way forward is probably to add ASIO support to CPAL.
Tomaka has mentioned that he's happy to see ASIO support added and that it should follow a platform-specific extensions API similar to the way winit and glutin do. See his comment here.
cc @freesig maybe we can work out some day after Monday next week (when I get back) to have a sesh and work on this together 👍
The text was updated successfully, but these errors were encountered: