-
Notifications
You must be signed in to change notification settings - Fork 150
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
Access to PortAudio's platform-specific API #85
base: master
Are you sure you want to change the base?
Conversation
Thanks a lot! This looks very promising, but I think there are also quite a few problems we still have to solve ... AFAIU, this keeps the Do you have an idea how the whole thing could be extended in order to solve the problem described in #55? |
Yes, the commits thus far do not change any current
Aside: I've been wondering about calling these .ExtraSettings instead of .Settings, to mirror the name of My rationale for putting .Settings (or .ExtraSettings) under .api. is because not all platform API's have equivalent settings structures available in PortAudio. But perhaps we could bring these names names up one level, such as:
and if a platform doesn't have an equivalent extra_settings type such as alsa, then we could leave it set to hostapis.alsa.ExtraSettings = None Anyhow, to address #55 and platform-specific try:
wasapi = sd.hostapis.wasapi
except AttributeError:
# wasapi is unavailable on this host / PortAudio installation
wasapi = None
else:
wasapi.default_extra_settings = wasapi.ExtraSettings(exclusive=True)
# Do similar for CoreAudio:
try:
coreaudio = sd.hostapis.coreaudio
except AttributeError:
# coreaudio is unavailable on this host / PortAudio installation
coreaudio = None
else:
coreaudio.default_extra_settings = coreaudio.ExtraSettings(change_device_parameters=True, fail_if_conversion_required=True) ? Then an application could cover all its bases, by including try/except/else blocks for several platforms in the same code. Does this sound promising? If so, I'm happy to tweak this PR to include it. (i.e., shall I move Assuming we need to stay compatible with the current |
We don't have to stay compatible with any of the I don't really like to have yet another place to specify default settings ... and jumping around between sd.hostapis.coreaudio.output_channel_map = -1, -1, 0, -1, 1, -1
sd.play(myarray, fs) And to address #55 (I guess this would take care of the concerns I mentioned over there): if sd.hostapis.wasapi:
sd.hostapis.wasapi.output_exclusive = True That's just brainstorming BTW, I didn't really think this through! We would have many input/output specific settings that way ... or we could try the 2-tuple thing I did with a few other settings? The idea behind this was that most of the time we don't care or we want the same settings for input and output anyway. The problem is with parameters that are iterables, like I'm not sure if we should really combine the existing coreaudio = sd.extra_settings.coreaudio
if coreaudio:
coreaudio.change_device_parameters = True, False The current Another thing: how to get quickly from a device to the new settings? Still just brainstorming, this probably doesn't make sense at all ... Over in #84 (comment) you mentioned:
Yeah, that sounds good, but doing all those if something.something.alsa:
# do alsa settings
if something.something.wasapi:
# do wasapi settings There could be two different things: the enumeration with the same IDs as PortAudio uses (using |
Hmm, as the library stands right now, can an application open two streams simultaneously? Perhaps even with something like this contrived example with two devices whose clocks having unsynchronized clocks:
Otherwise, I'm actually happy with how The part I don't see the need for is having any sort of specifiable defaults at all, so I'd lobby in the other direction to simplify But I admit that there are folks who'd rather set some of these parameters ahead of time, so they don't have the chore to pass around an extra struct. My personal use case for having defaults is if I'm trying something interactive with ipython. However, when I'm writing a larger application, I ideally like to keep my code as modular as possible, even up to the point of permitting folks concurrent access when feasible, but basically, having an underlying library that maintains shared-state between concurrent instantiations drives me crazy so I avoid them like the plague. With the current defaults in So, I'm just not a fan of globals when I want deterministic, modular scalability... Regarding How's this:
In [9]: for h in sd.hostapis:
...: print h.apiname
...:
coreaudio
In [12]: sd.hostapis.coreaudio
Out[12]: _HostAPI(name=u'Core Audio', devices=[0, 1], default_input_device=0, default_output_device=1, apiname='coreaudio', api=COREAUDIO(get_buffer_size_range=<function _api_coreaudio_get_buffer_size_range at 0x101abbcf8>, get_output_channel_name=<function _api_coreaudio_get_output_channel_name at 0x101abbc80>, get_input_channel_name=<function _api_coreaudio_get_input_channel_name at 0x101abbb90>, Settings=<class 'sounddevice.CoreAudioSettings'>))
In [13]: sd.hostapis.coreaudio.name
Out[11]: u'Core Audio'
In [16]: sd.hostapis.alsa
In [17]: sd.hostapis.alsa is None
Out[17]: True
In [18]: sd.hostapis.alsa.name
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-18-eff2123623a0> in <module>()
----> 1 sd.hostapis.alsa.name
AttributeError: 'NoneType' object has no attribute 'name'
In [19]: sd.hostapis.bob
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-19-aacc8a7224a6> in <module>()
----> 1 sd.hostapis.bob
AttributeError: 'HostAPIs' object has no attribute 'bob' I just pushed this to the PR, so now it does support the desire for: if sd.hostapis.alsa:
# do alsa settings
if sd.hostapis.wasapi:
# do wasapi settings because the try/catch/else checks are tedious. :) So getting back to:
I agree. :) So, I'd rather not store the settings themselves in You mentioned wanting to minimize having platform-specific stuff in the global namespace? By renaming those classes such as Or, I'm fine with also leaving the whole set of Settings classes as they are now, and not bother with moving them around namespaces. But if you wanted to minimize global clutter, this is one alternative. I think I could live with either ( Regarding defaults, Yes, if we're seriously considering permitting folks to set per-platform default Settings, then I think any implementation will start to look a bit complex. But the whole defaults behavior, is an addition on top of PortAudio. (PortAudio doesn't bother with defaults does it?) In short, I ideologically like being able to specify extra_settings when I actually create a stream, since it basically maps to calling |
Hmm, I'm still brainstorming on implementation details behind creating a sd.hostapis.*.default. For some reason |
Exactly, those are the right questions. I think it's perfectly fine to serialize calls for creating streams if they need different platform-specific settings. The main question for me is: do we preclude certain use cases if we force people to serialize? And I don't think so ...
That's true. I wouldn't say its binding, though.
I don't know how "modular" you'll really be in practice.
Duly noted. But as you say yourself, it is useful for interactive use.
And that's good. But I had yet another thing in mind when introducing the module-level defaults: import sounddevice as sd
import some_other_module
sd.default.device = 'my special sound card'
some_other_module.run() I don't know if such a case will ever happen, but I think it is a nice idea, isn't it?
Yes, sounds reasonable. The Python interpreter has global state. Most notably, the So let's face it, there is global state already. Sure, ideally we wouldn't. But for me, having global state or not is just one of many aspects we should weigh against each other to get an as "good" as possible API in the end.
Except that settings are already stored there, e.g. by
Yes, that would be nice, but it shouldn't be the main concern.
But this isn't really complex, is it? if sd.hostapis.wasapi:
sd.hostapis.wasapi.exclusive = True Creating a special object and passing it to the stream constructor seems more complex to me. Or are you talking about the complexity of the implementation?
Yes, it is an addition. And we'll see if some host API specific defaults will be considered worth adding. |
Yep, I was referring to the complexity of the implementation. :) Which, I'm also fine with, but I'm only slightly wary of long term code maintainability. (i.e., the ability for general humanity to grok the underlying plumbing.) So, I am fine with keeping the current global default ( If in the process of implementing hostapi-specific extra_settings that we find that the implementation gets to be a pain to support that extra
Yes, it is a nice idea. Nice enough to support api-specific defaults in my opinion. :) So let's keep defaults and I suspect that this particular use case would benefit from permitting hostapi-specific defaults. Ahh, instead of storing default extra_settings under import sounddevice as sd
import some_other_module
sd.default.device = 'my special sound card'
# Apply greedy performance defaults on various platforms:
sd.default.extra_settings.coreaudio = sd.CoreAudioSettings(
change_device_parameters=True, fail_if_conversion_required=True)
sd.default.extra_settings.wasapi = sd.WasapiSettings(exclusive=True)
...
some_other_module.run() Or would we like to get rid of the *Settings classes completely (at least from a user perspective), to support something like this: # Apply greedy performance defaults on various platforms:
sd.default.extra_settings.coreaudio.change_device_parameters=True
sd.default.extra_settings.coreaudio.fail_if_conversion_required=True
sd.default.extra_settings.wasapi.exclusive=True Basically, could we make it an error for a user to If anything for clarity, perhaps we'd search-n-replace replace # Apply greedy performance defaults on various platforms:
sd.default.hostapi_settings.coreaudio.change_device_parameters=True,True
sd.default.hostapi_settings.coreaudio.fail_if_conversion_required=True,True
sd.default.hostapi_settings.wasapi.exclusive=True,True Regarding PortAudio's thread support, it doesn't like they necessarily poo-poo the idea:
Would adding a mutex around the 2 calls to The discussion on a multi-thread safe extension to a cross-platform sound library such as |
I would like to clarify something. I'm not insisting on adding global state, what I'm saying is: "If the best API we can come up with adds global state, so be it." Putting the stream initialization parameters aside for a bit, let's talk about the other host-API-specific functionality you are mentioning in your original comment. The most pressing question for me is: how will a user access those features? I think the idea of using If we use attribute access, having both Are there other names we could use? |
To get an idea what we are dealing with, I've made (hopefully complete) list of all the currently available host-API-specific settings: #4 (comment). |
If we combine the host API enumeration and the host-API-specific features, we should consider that there are some host APIs that don't have specific settings, e.g. |
Ideally, whatever strikes some balance between user keystrokes and library maintenance. :) For example, 1 small change that could happen right now might be to change
This same change could be applied to have It just depends on if you want folks to be able to use slightly shorter syntax:
My own preference is for the slightly shorter user-syntax, especially since the returned dict keys is always the same for each of So here, my own preference would be for returning a namedtuple instead of a dict, and the functionality of namedtuple is close enough to dict for our use cases:
So, the functionality of namedtuple is on par with dict, but shortens user-keystrokes from
Your choice. :) I wager that identical access to the host-API-specific functions can be made through the current host-API enumeration mechanism As this PR stands right now, identical functionality is available from both
I don't see the need to keep both. I'm partial to the shorter notation of So regarding the broad category of "host-API-specific functionality", could we (or should we not?) divide this topic into two subareas? Specifically:
I was originally thinking along these lines, because the first category is only used during stream instantiation and not at other times, so I just assumed to keep these categories in 2 different realms to avoid ambiguity. At the moment, this PR does happen to keep these areas separate.
Are you pondering merging these 2 realms? For example, putting ASIO's Hmm, colocating CoreAudio's I'm taking a step back for a moment and asking "why the heck not?" Hmm... ASIO does have a curious function called OK... Why not? :) cringe... it feels dirty to "cross the streams" so to speak... but I'm not sure my own aversion is founded this time... So.... what if everything was thrown into the same namespace... Ahh, should things like ASIO's sd.devices[device_index].name
sd.devices[device_index].hostapi -> reference to sd.hostapis[...]
sd.devices[device_index].max_input_channels
sd.devices[device_index].samplerate -> defaults to default_samplerate
...
sd.devices[device_index].channel_map # this is only available on coreaudio devices
sd.devices[device_index].channel_selectors # this is only available on asio devices
... uh.. hm. sd.device = my_favorite_interface
sd.channels = 2
sd.channel_map = [2, 3], [-1, -1, 0, 1, -1, 0, -1, ...] # because this is a coreaudio device, so this specific name is available here with the quirky annoyance that the underlying PortAudio exposes from the underlying CoreAudio mapping requiring -1's in the output list?
sd.dither = True # This defaults to true anyway...
sd.prime_output_buffers_using_stream_callback = True # Would otherwise default to False...
with sd.StreamRaw as stream0:
.... ? Would sd.device = my_favorite_interface
...
with sd.StreamRaw as stream0:
sd.device = my_other_favorite_interface
with sd.StreamRaw as stream1:
... Would it still be nice if all of these relevant settings could be suppled at creation time, such as: # some_module_A.py
import sounddevice as sd
param_set0 = sd.StreamParameters()
param_set0.device = ...
...
param_set1 = sd.StreamParameters()
param_set1.device = ...
...
with sd.StreamRaw(param_set0) as stream0, sd.StreamRaw(param_set1) as stream1:
... And could we support your external-module-overriding-defaults use-case via something like being able to "push" default StreamParameters onto a stack? # some_module_B.py
import sounddevice as sd
param_set = sd.StreamParameters()
param_set.change_device_parameters = True # CoreAudio-specific
param_set.fail_if_conversion_required = True # CoreAudio-specific
param_set.exclusive = True # WASAPI-specific
sd.push_default_params(param_set)
import some_module_A
sd.pop_default_params()
# Or accomplish the same via a with-block to perform implicit pop:
with sd.push_default_params_context(param_set):
import some_module_A
# pop is implicitly performed here. And allow some way to layer-up the stack of defaults so that we could even allow yet another module to do the same to some_module_B? # some_module_C.py
import sounddevice as sd
param_set = sd.StreamParameters()
param_set.samplerate = 48000
with sd.push_default_params_context(param_set):
import some_module_B Perhaps the above could be implemented with # somewhere in the depths of
# class _StreamBase(object)
# __init__(self, stream_params=None):
# Pseudo-code:
final_stream_params = {}
# Layer on each stack level of default settings, so that
# settings in newer levels override those in older levels:
for stack_level in stack_of_defaults:
final_stream_params.update(stack_level._asdict())
# Now if the caller provided a stream_params as an argument to a class derived
# from _StreamBase, then we should apply their most recent stream_params on top:
if stream_params is not None:
final_stream_params.update(stream_params._asdict())
# Now go use the final_stream_params dict to create a stream... This would provide a way for some_module_A to open a stream with their explicit parameter sets, and then let any unspecified settings default to however the stack-of-defaults plays out from some_module_B and some_module_C. |
Oh man, I've really fallen behind D: Let me try and catch up here. I've listed some comments below to things I read that made my hairs raise :P One kind of general comment about this commit: I think this should focus on the change of API and re-implementing the existing host api extensions. We shouldn't just glob in brand new support for all these other untested host-api features. And as a side note, we really are going to need a test suite soon. Dammit. On to my specific comments..
You're kind of saying two different things here. There's nothing wrong with a mult-threaded application that uses portaudio. The authors are just saying "we do not provide thread safety to the API". I don't think that's a discouragement to use multiple threads, they just don't want to provide any concurrency guarantees. I think it's very discouraging (and unfounded) to say that people "just shouldn't use multiple threads". (In fact, you won't find any statement to that effect in the portaudio documentation).
Yes.
I don't think that's accurate. What if I have two different devices I want to play out from and I don't care if they're synchronized? I think that's a perfectly reasonable use case and one that portaudio does support. More complicated use cases of having multiple synchronized streams is also (reportedly) possible on some platforms, but I can't really speak to that.
I found an old listing here that spells out the libraries supported. AL is apparently Silicon Graphics Audio Library which seemingly met its demise quite a while ago. And SoundManager is a pre-osx audio api for mac. So I think it's okay to drop any semblance of support for these libraries.
I second that. I think it should only be removed if we can find another non-global way to implement configuration of these parameters. I see
I'm in agreement with this. |
I'm actually a fan of the (1) Host specific functions that are not necessarily tied to a particular stream like: PaAsio_GetInputChanneName, PaAsio_GetAvailableBufferSizes (and similar functions for alsa/coreaudio/etc.) (2) Host specific functions that are stream specific like PaAsio_GetInputChannelName or PaAlsa_GetStreamInputCard. (PaAsio_SetStreamSampleRate - a stream specific setter function - seems to be an outlier but should be treated similar to the getters). Now, I would think that ideally, from the perspective of a user, I would want settings in category (2) to be attributes or functions that are bound to the Stream class itself. But this I think would require that _StreamBase be replaced with a factory function/class that produces host specific Streams. So in the end you would have stream classes for each API. For example, you might implement GetStreamInputCard like this:
The obvious downside is that it would significantly increase i) code size, ii) complexity, and iii) namespace pollution. One way to address (iii) would be to put the host specifc stream classes in a submodule ( That's kind of the 'brute force' approach. Looking in the other direction, a very straightforward to implement approach would be to simply add the host API functions as static methods attached to the *Settings classes. That would mean usage would look something like this:
Which is kind of unintuitive and certainly unpythonic but is fully functional with minimal implementation and maintenance effort. I don't think either of these on their own are implementation worthy, but I'm just thinking out loud. |
Thanks a lot @dholl and @tgarc for the brainstorming and the many great suggestions! I'm OK with keeping I'm OK with renaming them, but only if it's worth the change. I would like to keep the portable features from PortAudio separate from the non-portable ones. I don't insist on being able to specify multiple
I'm very reluctant to add mutexes, most likely we make it worse with that. I think if users want to use multiple threads, they should be responsible for any necessary locking or whatever.
I think a namedtuple isn't a good match, since it doesn't really have I think we could get rid of myapi = sd.hostapis[0] This would automatically (if we also provide In addition to that, we could allow myapi = sd.hostapis['alsa'] This would raise a alsa = sd.hostapis.get('alsa')
if alsa:
alsa.whatever() Therefore, this object could be used like a I don't think attribute access (like I think it's important to provide a fixed set of host API strings in the documentation, to avoid confusion. Now the question is how such a "host API" object should look like. I don't really like the detour What about keeping
That's the question! I'm OK with having separate realms, there is just one special case:
Yes I am. But probably it's not a good idea.
I don't know. It indeed sounds cringeworthy.
This would take it to the extreme, and it sounds strange. Your example with with sd.Stream(**param_dict0):
... Now the thing with the parameter stack really seems to get out of control, but hey, we are brainstorming! So it's good! You basically can do all this already.
Well I already partially implemented this when introducing
Oh yes! Any volunteers?
You are right, my bad.
Sorry, I was wrong to say it like that. I would like to mainly advertise the platform-independent features, because that's what's great about PortAudio.
I dislike this idea quite a lot. Having host-API-specific stream classes would defeat the purpose of using a platform-independent library in the first place, wouldn't it? If you want to re-implement everything in Python, I think it would be better to actually use the platform-native APIs, like it's done in https://github.com/bastibe/Python-Audio. But I still think using PortAudio is the better strategy.
I'm surprised that you seem to strongly dislike global state, yet a class object having state would be OK ...
Yeah, indeed.
Right. But it is really ugly. Back to your points (1) and (2) above, what about the following? asio = sd.hostapis['asio']
name = asio.input_channel_name(1, 5)
a, b, c, d = asio.available_buffer_sizes(1)
asio.change_samplerate(mystream, 48000)
# and, on another system:
idx = sd.hostapis['alsa'].input_card(mystream) And to get rid of the ca_settings = sd.hostapis['coreaudio'].extra_settings(change_device_parameters=True, fail_if_conversion_required=True)
sd.play(myarray, extra_settings=ca_settings) This way, |
Uh, yes it does according to
Please please please read up on
The namedtuple-based implementation in this pull request already supports this. :)
Yep, the current PR supports
Yep the PR demonstrates this too -- including if someone really wants to use a strings like
But I'd recommend dropping the whole dict-step:
Note: This PR used to raise an AttributeError on unavailable hostapi's, until I implemented a small wrapper (just a subclass) to return Note that this commit does not (should not?) interfere with So with this commit, this syntax is currently supported: alsa = sd.hostapis.alsa
if alsa:
alsa.whatever() If someone doesn't like the None-behavior I added, you can still invoke the exception behavior by trying to access 1 level deeper into the hostapi's struct. In this example, I'm running on my laptop where I only have
Yep, please read up on
True. But is it possible for any of the returned data from
For interactive usage, I like that I can use tab-completion with the current PR:
A dict-style access couldn't do tab completion, right? How's that for interactive user experience? :)
Yep. That's easilly fixed. Which doc string should I tweak? :) Given that the binding to each What if I add some descriptive text to the doc string I already created on sd.hostapis? :)
So this last example can already be rewritten as: asio = sd.hostapis.asio.api
name = asio.get_input_channel_name(1, 5)
a, b, c, d = asio.get_available_buffer_sizes(1)
asio.set_stream_sample_rate(mystream, 48000)
# and, on another system:
idx = sd.hostapis.alsa.api.get_stream_input_card(mystream)
And again, this is also directly supported in the current PR: ca_settings = sd.hostapis.coreaudio.api.Settings(change_device_parameters=True, fail_if_conversion_required=True)
sd.play(myarray, extra_settings=ca_settings)
Heh, I wish it could be a raw CFFI structure. :) But unfortunately, the raw structures to contain pointers to other raw CFFI data sush as However, I do think it is technically possible to rearrange the current code a little bit to replace the Implementation note: Instantiating a namedtuple requires that a user specify all fields, but I think this would get annoying to a library user such as if they don't want to specify everything, especially in the case of coreaudio's Settings. So I'd tweak the namedtuple to default unspecified fields to None, so that a user doesn't need to specify every setting. For an example, check out http://stackoverflow.com/questions/11351032/named-tuple-and-optional-keyword-arguments |
@dholl Sorry that I was unclear, there seems to have been a misunderstanding.
I see now where you misunderstood me, because I was too unclear: you thought that "it" means To clarify my position: I think a Because the set of host API properties doesn't have
No need, I know I know that your current suggestion supports indexing and iteration, and that's good. In general, supporting attribute access would also be interesting, but we definitely need a way to find out if a given host API is currently available or not. I agree that attribute access is easier to type in most cases, but I think that's not important here, since this will seldom be needed in interactive use cases.
Sorry, I was unclear again. Saying "could be used like a
I'm quite sure it isn't.
It is indeed simple, but I don't like it. But if there is a good reason, I'm all for it!
Yes I know. I'm a fan of tab completion. Do you have a use case in mind where this would be the case? BTW, your screenshots also show one of the abovementioned drawbacks of
I think I've read about ways to get that. Some Python shells can do that given some additional information. I didn't dig into that, it sounded complicated.
Oh yes, I didn't think about that! Indeed all structures and arrays created with I don't know if there is some other way around, I fear there isn't.
You seem to be obsessed with |
About keeping arrays and structs (that are part of other structs) alive: I've just read about a trick using I didn't try it but I think it looks promising, probably we could avoid some wrapper classes with this! |
How so? Also to clarify, I'm currently using namedtuple for two different purposes:
Use case 2 is probably where we differ, correct? But what do you think about case 1?
True. That looks like a side affect of python using But available hostapis do not show up during iteration:
Yep, I am a fan of named tuple, and do like that it's implemented in Python, and so enjoy that it already exists so that I don't need to re-invent it when I just want something with struct-like behavior with minimal memory overhead. (but not relevant in this case for only a few instantiated objects) :)
Nifty! That looks promising. |
Because I don't see them as an ordered list like When I said "I think a For use case 2 I don't think it's worth (but of course possible) adding attribute access, and for use case 1 I think it's not worth (but still possible) adding numeric indexing. While you seem to think about how simple the implementation would be, I think about how much unnecessary API dead weight this would add. Having said that, this is only a minor detail of this whole endeavor.
The last point is what I don't really like in your current proposal, because you are forcing the IMHO unnecessary indirection via
I'm all for not re-inventing stuff, but if the
If you want struct-like behavior without tuple-like behavior, you'd be better off with types.SimpleNamespace, which sadly only exists in Python 3. You could use an argparse.Namespace for compatibility, but it's a bit awkward to get this from the
You can have that with your own class, too. |
Sure, all settings are technically "host API settings" but I think it's useful to make the distinction that, while all other settings are supported across all platforms, "extra_settings" are only available on one particular host api. Portaudio also uses the term
Yeah I think I was a little unclear. I was trying to say more what you were saying: that we don't need to implement all these features in this commit but we should consider all the features in making decisions about this commit.
I think you're crossing wires here. I didn't say anything about global state. Besides, I was just suggesting using static methods for non-stream-specific host api extensions that already use global state (e.g. Alsa's SetNumPeriods is global). No getting around that.
I strongly agree - there's no reason I can see that we should go there. As soon as we do we have to start giving out guarantees on thread safety and no..let's not do that :X
I like it. Seems natural enough, and doesn't add too much complexity. Assuming the
:oo that would be cool but how would we document the host api features then? |
Where does it use "host api" for that? There is If we consequently want to use PortAudio's names, we'd have to use
Indeed, that's still to be found out ...
Well instead of a class constructor we would use a function/method to create those CFFI structures (as I mentioned in the very end of #85 (comment)). And that's where we can document the details. |
OK, so scrapping the Assuming
|
Why not get rid of the need for a name at all? asio = sd.hostapis['asio']
chan0_name = asio.get_input_channel_name(device, chan=0)
asio.set_stream_sample_rate(stream, sample_rate) See the end of #85 (comment). |
I created a branch in my repo fork called "hostapis", that I'm eager to start discussing. It enables access to PortAudio's platform-specific API in 2 alternative fashions: (Note, the first syntax needs PR #84 for
name=
support withquery_hostapis()
)The first approach involves a more traditional interface by just extending
query_hostapis()
to return an additional dictionary key,'api'
.The second approach introduces a new item to the global namespace,
hostapis
.Of course this branch's code has a bunch of style issues..., but at first, I'm looking forward to at least start exploring these strategies or brainstorming upon some other approaches. The underlying implementation is certainly open for debate (namedtuple's galore!), but I hope that an open code-sharing / peer review will ameliorate the ugliness, once we settle on how we want the external interface to operate. :)
Example usage:
Here's a list of host-API-specific features that I've implemented so far:
I've only tested
coreaudio
, and I'd love for other folks to test the others if they already have a hardware setup. I'm open to handing out contributor access to this branch in my fork to expedite the work flow.