Closed freesig closed 6 years ago
Actually I think the reason this isn't working is just because of this:
let mut asio_drivers = ai::AsioDrivers::new();
let load_result = asio_drivers.loadDriver(raw);
// Take back ownership
my_driver_name = CString::from_raw(raw);
if !load_result {
return Err(ASIOError::DriverLoadError);
}
Where "raw" is the name of the driver. So it's getting back false from loadDriver but that's only because it's already active not because it failed to load.
Actually we do need to store the AsioDriver once it is initialized because the destructor for this class removes the driver.
AsioDrivers::~AsioDrivers()
{
removeCurrentDriver();
}
Todo
Ok I still have the issue of how to share this AsioDriver between streams. I can't change the way that streams are created. Consider this:
let output_stream = app.audio.new_output_stream(output_model, audio_out).build().unwrap();
let input_stream = app.audio.new_input_stream(input_model, audio_in).build().unwrap();
The AsioDriver is inside both the output_stream and input_stream. I need some way to share it without changing the api.
Idealy it would happen in the EventLoop::new() But this would require some sort of global which is pretty bad.
Ok I've done a bit of a rewrite and now the drivers live in the device. I just need a way to share them across streams and this issue will be fixed.
Yeah I think the way global state is normally managed in low level crates like this is using a lazy_static!
with a Mutex
around it. This way at least you can't accidentally cause any data races.
I think it's good to avoid spawning threads in low-level crates like this when possible as native threads can be pretty expensive on some platforms. E.g. I think on macOS the default native thread stack size is like 2MB that needs to be allocated. You can adjust the stack size manually, but anyways I think an extra thread might be overkill for this case.
no worries, I'll give that a go now.
So the way I had this working was that a Drivers struct was created that holds the drivers. Then you call the functions through that struct. This ensures you can't call a function that needs an active driver without the driver being active. However with a lazy_static Mutex you end up in this really awkward place where you need to check the value is active with every call like this:
fn get_channels() -> Result<(), AsioError> {
match *ASIO_DRIVERS.lock().unwrap() {
Some(_) => {
//Do some work
Ok(())
}
None => Err( NoDriverError )
}
}
It's pretty messy and error prone. Like if someone adds a new function and forgets to add this then we have a problem. Am I missing something or is this necessary?
Actually I think I found an ok solution
This still doesn't work with Nannou. I think something in Nannou is dropping the output stream when an input stream is created
It seems strange that this would be the case when nannou doesn't have any platform or OS specific code, and simultaneous input/output works with the other backends. I made a CPAL feedback example for testing this a little while back - it might be useful to test that it works for sure in CPAL itself first?
So I just tried making an "echo" example in Nannou with ASIO. It just records the input buffer and puts it into the output. However only the first stream will start. Which ever one you call build on first. I'll start investigating why this is.
So it looks like the first call to AsioDriver::new() creates the drivers. It is stored inside the AsioStream.
So we just need to share this between the different active streams. Maybe like