Skip to content
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

Introduce a `Host` API #289

Merged
merged 8 commits into from Jun 25, 2019
Merged

Introduce a `Host` API #289

merged 8 commits into from Jun 25, 2019

Conversation

@mitchmindtree
Copy link
Member

mitchmindtree commented Jun 24, 2019

This is an implementation of the API described at #204. Please see that
issue for more details on the motivation.


A Host provides access to the available audio devices on the system.
Some platforms have more than one host available, e.g.
wasapi/asio/dsound on windows, alsa/pulse/jack on linux and so on. As a
result, some audio devices are only available on certain hosts, while
others are only available on other hosts. Every platform supported by
CPAL has at least one DefaultHost that is guaranteed to be available
(alsa, wasapi and coreaudio). Currently, the default hosts are the only
hosts supported by CPAL, however this will change as of landing #221 (cc
@freesig). These changes should also accommodate support for other hosts
such as jack #250 (cc @derekdreery) and pulseaudio (cc @knappador) #259.

This introduces a suite of traits allowing for both compile time and
runtime dispatch of different hosts and their uniquely associated device
and event loop types.

A new private host module has been added containing the individual
host implementations, each in their own submodule gated to the platforms
on which they are available.

A new platform module has been added containing platform-specific
items, including a dynamically dispatched host type that allows for
easily switching between hosts at runtime.

The ALL_HOSTS slice contains a HostId for each host supported on
the current platform. The available_hosts function produces a
HostId for each host that is currently available on the platform.
The host_from_id function allows for initialising a host from its
associated ID, failing with a HostUnavailable error. The
default_host function returns the default host and should never
fail.

Please see the examples for a demonstration of the change in usage. For
the most part, things look the same at the surface level, however the
role of device enumeration and creating the event loop have been moved
from global functions to host methods. The enumerate.rs example has been
updated to enumerate all devices for each host, not just the default.

TODO

  • Add the new Host API
  • Update examples for the new API.
  • Null host
  • ALSA host
  • WASAPI host
  • CoreAudio host
  • Emscripten host

Follow-up PR

cc @ishitatsuyuki more to review for you if you're interested.

This is an implementation of the API described at #204. Please see that
issue for more details on the motivation.

-----

A **Host** provides access to the available audio devices on the system.
Some platforms have more than one host available, e.g.
wasapi/asio/dsound on windows, alsa/pulse/jack on linux and so on. As a
result, some audio devices are only available on certain hosts, while
others are only available on other hosts. Every platform supported by
CPAL has at least one **DefaultHost** that is guaranteed to be available
(alsa, wasapi and coreaudio). Currently, the default hosts are the only
hosts supported by CPAL, however this will change as of landing #221 (cc
@freesig). These changes should also accommodate support for other hosts
such as jack #250 (cc @derekdreery) and pulseaudio (cc @knappador) #259.

This introduces a suite of traits allowing for both compile time and
runtime dispatch of different hosts and their uniquely associated device
and event loop types.

A new private **host** module has been added containing the individual
host implementations, each in their own submodule gated to the platforms
on which they are available.

A new **platform** module has been added containing platform-specific
items, including a dynamically dispatched host type that allows for
easily switching between hosts at runtime.

The **ALL_HOSTS** slice contains a **HostId** for each host supported on
the current platform. The **available_hosts** function produces a
**HostId** for each host that is currently *available* on the platform.
The **host_from_id** function allows for initialising a host from its
associated ID, failing with a **HostUnavailable** error. The
**default_host** function returns the default host and should never
fail.

Please see the examples for a demonstration of the change in usage. For
the most part, things look the same at the surface level, however the
role of device enumeration and creating the event loop have been moved
from global functions to host methods. The enumerate.rs example has been
updated to enumerate all devices for each host, not just the default.

**TODO**

- [x] Add the new **Host** API
- [x] Update examples for the new API.
- [x] ALSA host
- [ ] WASAPI host
- [ ] CoreAudio host
- [ ] Emscripten host **Follow-up PR**
- [ ] ASIO host #221

cc @ishitatsuyuki more to review for you if you're interested, but it
might be easier after #288 lands and this gets rebased.
Also addresses some other CI errors:

- Add Host::new constructor for null backend
- Add missing DevicesError import to coreaudio backend
@mitchmindtree mitchmindtree force-pushed the mitchmindtree:host branch from 879c91e to 6e9b40e Jun 24, 2019
@mitchmindtree mitchmindtree changed the title [WIP] Introduce a `Host` API Introduce a `Host` API Jun 24, 2019
@mitchmindtree mitchmindtree force-pushed the mitchmindtree:host branch from 740f714 to e131979 Jun 24, 2019
Copy link
Collaborator

ishitatsuyuki left a comment

This was a well-designed implementation! The review comments are mainly a few nits.

I’m slightly concerned regarding the big macro generating dynamic dispatch code, but well there is a use case for this and I can’t think a better way of implementation. So yeah, let’s go with this!

use Format;
use Host as HostTrait;

This comment has been minimized.

Copy link
@ishitatsuyuki

ishitatsuyuki Jun 25, 2019

Collaborator

Personally I’d prefer names like AlsaHost and avoid renaming imports, but I’ll leave it up to you

This comment has been minimized.

Copy link
@mitchmindtree

mitchmindtree Jun 25, 2019

Author Member

Yes I normally would agree, but in this case having each of the private backend types was necessary to keep the dynamic dispatch code generation much easier. I have re-exported the platform-specific types in the platform_impl module with proper names as you describe (e.g. Host as AlsaHost) so that users can still use these directly with friendlier names if they require :)

type EventLoop = EventLoop;

fn is_available() -> bool {
// Assume ALSA is always available on linux/freebsd.

This comment has been minimized.

Copy link
@ishitatsuyuki

ishitatsuyuki Jun 25, 2019

Collaborator

Copy paste leftover

src/lib.rs Outdated
///
/// Another great example is the Linux platform. While the ALSA host API is the lowest-level API
/// available to almost all distributions of Linux, its flexibility is limited as it requires that
/// each process have exclusive access to the devices with which they establish streams. PortAudio

This comment has been minimized.

Copy link
@ishitatsuyuki

ishitatsuyuki Jun 25, 2019

Collaborator

PulseAudio

@derekdreery

This comment has been minimized.

Copy link
Contributor

derekdreery commented Jun 25, 2019

I'm excited by this. After it lands, I'd like to take a look at the alsa implementation to see if I can improve it. I think we cut some corners with regards to selection of parameters for the connection, with the consequence that we have panics and some valid configurations of audio hardware fail to work.

@ishitatsuyuki

This comment has been minimized.

Copy link
Collaborator

ishitatsuyuki commented Jun 25, 2019

One thing: I think DefaultHost should be replaced in favor of the polymorphic version, therefore the defaults should be determined at runtime.

This is because on Linux the preferred audio API varies a lot, depending on the audio server installed (PulseAudio or Jack) or even drivers (some users may be using OSS).

Re-exports host-specific types so that they are available within the
platform module if necessary (e.g. host::asla::Host as AlsaHost).

Allows for converting platform-specific host types (e.g. AlsaHost) into
the dynamically dispatched type generated for the target platform
(`Host`).
@mitchmindtree

This comment has been minimized.

Copy link
Member Author

mitchmindtree commented Jun 25, 2019

One thing: I think DefaultHost should be replaced in favor of the polymorphic version, therefore the defaults should be determined at runtime.

This is because on Linux the preferred audio API varies a lot, depending on the audio server installed (PulseAudio or Jack) or even drivers (some users may be using OSS).

Yes this is a good point, I've added a commit that makes this change.

@mitchmindtree mitchmindtree merged commit 7e413cb into RustAudio:master Jun 25, 2019
5 checks passed
5 checks passed
ci/circleci: android-test Your tests passed on CircleCI!
Details
ci/circleci: asmjs-test Your tests passed on CircleCI!
Details
ci/circleci: wasm-test Your tests passed on CircleCI!
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@mitchmindtree mitchmindtree deleted the mitchmindtree:host branch Jun 25, 2019
mitchmindtree added a commit to mitchmindtree/cpal that referenced this pull request Jul 5, 2019
This is quite a significant update for CPAL, including a number of
breaking changes. Here is a list of the breaking changes along with
links to where you can find more information:

- A `Host` API has been introduced in RustAudio#289 along with a follow-up
  refactor in RustAudio#295. Please see the examples for a demonstration of how
  to update your code. The necessary changes should hopefully be
  minimal. If this has caused you any major difficulty please let us
  know in an issue!
- An ASIO host has been introduced in RustAudio#292. This adds support for
  Steinberg's ASIO audio driver API on Windows. Please see the ASIO
  section of the README for more information on how to setup CPAL with
  ASIO support for your project.
- The user callback API has been overhauled to emit `StreamEvent`s
  rather than buffers in order to support handling stream errors. RustAudio#288.
- Error handling in general was overhauled in RustAudio#286. This meant taking
  advantage of the `failure` crate and adding support for
  backend-specific errors with custom messages. Many unnecessary
  `panic!`s have been removed, but a few remain that would indicate bugs
  in CPAL.

In general, checking out the updated examples will be the easiest way to
get a quick overview on how you can update your own code for these
changes.

The CHANGELOG.md has been updated to include these changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

3 participants
You can’t perform that action at this time.