mirror of
https://github.com/larsimmisch/pyalsaaudio.git
synced 2026-06-01 10:57:01 +00:00
Allow card index or device name for mixers.
This change breaks API compatibility. Sorry.
This commit is contained in:
+48
-33
@@ -91,19 +91,6 @@ char *translate_cardname(char *name)
|
|||||||
return full;
|
return full;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Translate a card index to a ALSA cardname
|
|
||||||
|
|
||||||
Returns a newly allocated string.
|
|
||||||
*/
|
|
||||||
char *translate_cardidx(int idx)
|
|
||||||
{
|
|
||||||
char name[32];
|
|
||||||
|
|
||||||
sprintf(name, "hw:%d", idx);
|
|
||||||
|
|
||||||
return strdup(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************/
|
/******************************************/
|
||||||
/* PCM object wrapper */
|
/* PCM object wrapper */
|
||||||
/******************************************/
|
/******************************************/
|
||||||
@@ -927,26 +914,39 @@ alsamixer_gethandle(char *cardname, snd_mixer_t **handle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
alsamixer_list(PyObject *self, PyObject *args)
|
alsamixer_list(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
snd_mixer_t *handle;
|
snd_mixer_t *handle;
|
||||||
snd_mixer_selem_id_t *sid;
|
snd_mixer_selem_id_t *sid;
|
||||||
snd_mixer_elem_t *elem;
|
snd_mixer_elem_t *elem;
|
||||||
int err;
|
int err;
|
||||||
int cardidx = 0;
|
int cardidx = -1;
|
||||||
char cardname[32];
|
char hw_device[32];
|
||||||
|
char *device = "default";
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args,"|i:mixers",&cardidx))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
sprintf(cardname, "hw:%d", cardidx);
|
|
||||||
|
|
||||||
|
char *kw[] = { "device", "cardindex", NULL };
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|si", kw,
|
||||||
|
&device, &cardidx))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (cardidx >= 0) {
|
||||||
|
if (cardidx >= 0 && cardidx < 32) {
|
||||||
|
snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_SetString(ALSAAudioError, "Invalid card number");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
device = hw_device;
|
||||||
|
}
|
||||||
|
|
||||||
snd_mixer_selem_id_alloca(&sid);
|
snd_mixer_selem_id_alloca(&sid);
|
||||||
err = alsamixer_gethandle(cardname, &handle);
|
err = alsamixer_gethandle(device, &handle);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
{
|
{
|
||||||
PyErr_SetString(ALSAAudioError,snd_strerror(err));
|
PyErr_SetString(ALSAAudioError, snd_strerror(err));
|
||||||
snd_mixer_close(handle);
|
snd_mixer_close(handle);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -991,22 +991,35 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||||||
{
|
{
|
||||||
alsamixer_t *self;
|
alsamixer_t *self;
|
||||||
int err;
|
int err;
|
||||||
int cardindex = 0;
|
|
||||||
char *control = "Master";
|
char *control = "Master";
|
||||||
|
char *device = "default";
|
||||||
|
char hw_device[32];
|
||||||
|
int cardidx = -1;
|
||||||
int id = 0;
|
int id = 0;
|
||||||
snd_mixer_elem_t *elem;
|
snd_mixer_elem_t *elem;
|
||||||
int channel;
|
int channel;
|
||||||
char *kw[] = { "control", "id", "cardindex", NULL };
|
char *kw[] = { "control", "id", "device", "cardindex", NULL };
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sii", kw,
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sisi", kw,
|
||||||
&control, &id, &cardindex))
|
&control, &id, &device, &cardidx))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (cardidx >= 0) {
|
||||||
|
if (cardidx >= 0 && cardidx < 32) {
|
||||||
|
snprintf(hw_device, sizeof(hw_device), "hw:%d", cardidx);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_SetString(ALSAAudioError, "Invalid card number");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
device = hw_device;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(self = (alsamixer_t *)PyObject_New(alsamixer_t, &ALSAMixerType)))
|
if (!(self = (alsamixer_t *)PyObject_New(alsamixer_t, &ALSAMixerType)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
self->handle = 0;
|
self->handle = 0;
|
||||||
self->cardname = translate_cardidx(cardindex);
|
self->cardname = strdup(device);
|
||||||
|
|
||||||
err = alsamixer_gethandle(self->cardname, &self->handle);
|
err = alsamixer_gethandle(self->cardname, &self->handle);
|
||||||
if (err<0)
|
if (err<0)
|
||||||
@@ -1019,15 +1032,17 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||||||
self->controlname = strdup(control);
|
self->controlname = strdup(control);
|
||||||
self->controlid = id;
|
self->controlid = id;
|
||||||
|
|
||||||
elem = alsamixer_find_elem(self->handle,control,id);
|
elem = alsamixer_find_elem(self->handle,control, id);
|
||||||
if (!elem)
|
if (!elem)
|
||||||
{
|
{
|
||||||
char errtext[128];
|
char errtext[128];
|
||||||
sprintf(errtext,"Unable to find mixer control '%s',%i",
|
sprintf(errtext,"Unable to find mixer control '%s',%i on card '%s'",
|
||||||
self->controlname,
|
self->controlname,
|
||||||
self->controlid);
|
self->controlid,
|
||||||
|
self->cardname);
|
||||||
snd_mixer_close(self->handle);
|
snd_mixer_close(self->handle);
|
||||||
PyErr_SetString(ALSAAudioError,errtext);
|
PyErr_SetString(ALSAAudioError,errtext);
|
||||||
|
free(self->cardname);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Determine mixer capabilities */
|
/* Determine mixer capabilities */
|
||||||
@@ -2050,8 +2065,8 @@ static PyTypeObject ALSAMixerType = {
|
|||||||
/******************************************/
|
/******************************************/
|
||||||
|
|
||||||
static PyMethodDef alsaaudio_methods[] = {
|
static PyMethodDef alsaaudio_methods[] = {
|
||||||
{ "cards", alsacard_list, METH_VARARGS, cards_doc},
|
{ "cards", (PyCFunction)alsacard_list, METH_VARARGS, cards_doc},
|
||||||
{ "mixers", alsamixer_list, METH_VARARGS, mixers_doc},
|
{ "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc},
|
||||||
{ 0, 0 },
|
{ 0, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+64
-22
@@ -38,11 +38,23 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
|||||||
|
|
||||||
List the available cards by name (suitable for PCM objects).
|
List the available cards by name (suitable for PCM objects).
|
||||||
|
|
||||||
.. function:: mixers([cardindex])
|
.. function:: mixers(device='default', cardindex=-1)
|
||||||
|
|
||||||
List the available mixers. The optional *cardindex* specifies which card
|
List the available mixers. The arguments are:
|
||||||
should be queried. The default is 0.
|
|
||||||
|
|
||||||
|
*device* - the name of the device on which the mixer resides. The default is
|
||||||
|
'default'.
|
||||||
|
|
||||||
|
*cardindex* - specifies which card should be used [#f3]_. If this argument
|
||||||
|
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||||
|
the `device` keyword argument is ignored. 0 is the
|
||||||
|
first sound card.
|
||||||
|
|
||||||
|
**Note:** The arguments for this function were changed in
|
||||||
|
version 0.8 and this change is not completely backward compatible.
|
||||||
|
Old versions accepted just the optional parameter *cardindex*.
|
||||||
|
The current version accepts either a device name or a cardindex.
|
||||||
|
|
||||||
.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, card='default')
|
.. class:: PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, card='default')
|
||||||
|
|
||||||
This class is used to represent a PCM device (both for playback and
|
This class is used to represent a PCM device (both for playback and
|
||||||
@@ -52,14 +64,24 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
|||||||
* *mode* - can be either ``PCM_NONBLOCK``, or ``PCM_NORMAL`` (default).
|
* *mode* - can be either ``PCM_NONBLOCK``, or ``PCM_NORMAL`` (default).
|
||||||
* *card* - specifies the name of the card that should be used.
|
* *card* - specifies the name of the card that should be used.
|
||||||
|
|
||||||
.. class:: Mixer(control='Master', id=0, cardindex=0)
|
.. class:: Mixer(control='Master', id=0, device='default', cardindex=-1)
|
||||||
|
|
||||||
This class is used to access a specific ALSA mixer. The arguments
|
This class is used to access a specific ALSA mixer. The arguments
|
||||||
are:
|
are:
|
||||||
|
|
||||||
* *control* - Name of the chosen mixed (default is 'Master').
|
* *control* - Name of the chosen mixed (default is 'Master').
|
||||||
* *id* - id of mixer -- More explanation needed here
|
* *id* - id of mixer -- More explanation needed here
|
||||||
* *cardindex* specifies which card should be used.
|
*device* - the name of the device on which the mixer resides. The default is
|
||||||
|
'default'.
|
||||||
|
*cardindex* - specifies which card should be used [#f3]_. If this argument
|
||||||
|
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||||
|
the `device` keyword argument is ignored. 0 is the
|
||||||
|
first sound card.
|
||||||
|
|
||||||
|
**Note:** The arguments for this constructor were changed in
|
||||||
|
version 0.8 and this change is not completely backward compatible.
|
||||||
|
Old versions accepted just the optional parameter *cardindex*.
|
||||||
|
The current version accepts either a device name or a cardindex.
|
||||||
|
|
||||||
.. exception:: ALSAAudioError
|
.. exception:: ALSAAudioError
|
||||||
|
|
||||||
@@ -243,7 +265,7 @@ Mixer Objects
|
|||||||
Mixer objects provides access to the ALSA mixer API.
|
Mixer objects provides access to the ALSA mixer API.
|
||||||
|
|
||||||
|
|
||||||
.. class:: Mixer(control='Master', id=0, cardindex=0)
|
.. class:: Mixer(control='Master', id=0, device='default', cardindex=-1)
|
||||||
|
|
||||||
*control* - specifies which control to manipulate using this mixer
|
*control* - specifies which control to manipulate using this mixer
|
||||||
object. The list of available controls can be found with the
|
object. The list of available controls can be found with the
|
||||||
@@ -252,10 +274,19 @@ Mixer objects provides access to the ALSA mixer API.
|
|||||||
|
|
||||||
*id* - the id of the mixer control. Default is 0
|
*id* - the id of the mixer control. Default is 0
|
||||||
|
|
||||||
*cardindex* - specifies which card should be used [#f3]_. 0 is the
|
*device* - the name of the device on which the mixer resides. The default is
|
||||||
first sound card.
|
'default'.
|
||||||
|
|
||||||
|
|
||||||
|
*cardindex* - specifies which card should be used [#f3]_. If this argument
|
||||||
|
is given, the device name is constructed like this: `hw:<cardindex>` and
|
||||||
|
the `device` keyword argument is ignored. 0 is the
|
||||||
|
first sound card.
|
||||||
|
|
||||||
|
**Note:** The arguments for this function were changed in
|
||||||
|
version 0.8 and this change is not completely backward compatible.
|
||||||
|
Old versions accepted just the optional parameter *cardindex*.
|
||||||
|
The current version accepts either a device name or a cardindex.
|
||||||
|
|
||||||
**Note:** For a list of available controls, you can also use **amixer**::
|
**Note:** For a list of available controls, you can also use **amixer**::
|
||||||
|
|
||||||
amixer
|
amixer
|
||||||
@@ -471,8 +502,8 @@ or::
|
|||||||
>>> import alsaaudio
|
>>> import alsaaudio
|
||||||
>>> alsaaudio.cards()
|
>>> alsaaudio.cards()
|
||||||
|
|
||||||
mixertest.py accepts the commandline option *-c <cardindex>*. Card
|
mixertest.py accepts the commandline options *-d <device> and
|
||||||
indices start at 0.
|
*-c <cardindex>*.
|
||||||
|
|
||||||
playwav.py
|
playwav.py
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
@@ -502,11 +533,13 @@ Play back the recording with::
|
|||||||
mixertest.py
|
mixertest.py
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
Without arguments, **mixertest.py** will list all available *controls*.
|
Without arguments, **mixertest.py** will list all available *controls* on the
|
||||||
|
default soundcard.
|
||||||
|
|
||||||
The output might look like this::
|
The output might look like this::
|
||||||
|
|
||||||
$ ./mixertest.py
|
$ ./mixertest.py
|
||||||
Available mixer controls:
|
Available mixer controls:
|
||||||
'Master'
|
'Master'
|
||||||
'Master Mono'
|
'Master Mono'
|
||||||
'Headphone'
|
'Headphone'
|
||||||
@@ -525,25 +558,34 @@ The output might look like this::
|
|||||||
With a single argument - the *control*, it will display the settings of
|
With a single argument - the *control*, it will display the settings of
|
||||||
that control; for example::
|
that control; for example::
|
||||||
|
|
||||||
$ ./mixertest.py Master
|
$ ./mixertest.py Master
|
||||||
Mixer name: 'Master'
|
Mixer name: 'Master'
|
||||||
Capabilities: Playback Volume Playback Mute
|
Capabilities: Playback Volume Playback Mute
|
||||||
Channel 0 volume: 61%
|
Channel 0 volume: 61%
|
||||||
Channel 1 volume: 61%
|
Channel 1 volume: 61%
|
||||||
|
|
||||||
With two arguments, the *control* and a *parameter*, it will set the
|
With two arguments, the *control* and a *parameter*, it will set the
|
||||||
parameter on the mixer::
|
parameter on the mixer::
|
||||||
|
|
||||||
$ ./mixertest.py Master mute
|
$ ./mixertest.py Master mute
|
||||||
|
|
||||||
This will mute the Master mixer.
|
This will mute the Master mixer.
|
||||||
|
|
||||||
Or::
|
Or::
|
||||||
|
|
||||||
$ ./mixertest.py Master 40
|
$ ./mixertest.py Master 40
|
||||||
|
|
||||||
This sets the volume to 40% on all channels.
|
This sets the volume to 40% on all channels.
|
||||||
|
|
||||||
|
To select a different soundcard, use either the *device* or *cardindex*
|
||||||
|
argument::
|
||||||
|
|
||||||
|
$ ./mixertest.py -c 0 Master
|
||||||
|
Mixer name: 'Master'
|
||||||
|
Capabilities: Playback Volume Playback Mute
|
||||||
|
Channel 0 volume: 61%
|
||||||
|
Channel 1 volume: 61%
|
||||||
|
|
||||||
.. rubric:: Footnotes
|
.. rubric:: Footnotes
|
||||||
|
|
||||||
.. [#f1] ALSA also allows ``PCM_ASYNC``, but this is not supported yet.
|
.. [#f1] ALSA also allows ``PCM_ASYNC``, but this is not supported yet.
|
||||||
|
|||||||
+3
-3
@@ -2,10 +2,10 @@
|
|||||||
Introduction
|
Introduction
|
||||||
************
|
************
|
||||||
|
|
||||||
:Author: Casper Wilstrup
|
:Author: Casper Wilstrup <cwi@aves.dk>
|
||||||
:Author: Lars Immisch
|
:Author: Lars Immisch <lars@ibp.de>
|
||||||
|
|
||||||
.. |release| replace:: 0.4
|
.. |release| replace:: 0.8
|
||||||
|
|
||||||
.. % At minimum, give your name and an email address. You can include a
|
.. % At minimum, give your name and an email address. You can include a
|
||||||
.. % snail-mail address if you like.
|
.. % snail-mail address if you like.
|
||||||
|
|||||||
+22
-24
@@ -18,39 +18,35 @@
|
|||||||
## python mixertest.py Capture 0,[un]rec # [dis/en]able capture on channel 0
|
## python mixertest.py Capture 0,[un]rec # [dis/en]able capture on channel 0
|
||||||
|
|
||||||
|
|
||||||
# Footnote: I'd normally use print instead of sys.std(out|err).write,
|
|
||||||
# but we're in the middle of the conversion between python 2 and 3
|
|
||||||
# and this code runs on both versions without conversion
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import getopt
|
import getopt
|
||||||
import alsaaudio
|
import alsaaudio
|
||||||
|
|
||||||
def list_mixers(idx=0):
|
def list_mixers(kwargs):
|
||||||
sys.stdout.write("Available mixer controls:\n")
|
print("Available mixer controls:")
|
||||||
for m in alsaaudio.mixers(idx):
|
for m in alsaaudio.mixers(**kwargs):
|
||||||
sys.stdout.write(" '%s'\n" % m)
|
print(" '%s'\n" % m)
|
||||||
|
|
||||||
def show_mixer(name, idx=0):
|
def show_mixer(name, kwargs):
|
||||||
# Demonstrates how mixer settings are queried.
|
# Demonstrates how mixer settings are queried.
|
||||||
try:
|
try:
|
||||||
mixer = alsaaudio.Mixer(name, cardindex=idx)
|
mixer = alsaaudio.Mixer(name, **kwargs)
|
||||||
except alsaaudio.ALSAAudioError:
|
except alsaaudio.ALSAAudioError:
|
||||||
sys.stderr.write("No such mixer\n")
|
sys.stderr.write("No such mixer\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
sys.stdout.write("Mixer name: '%s'\n" % mixer.mixer())
|
print("Mixer name: '%s'" % mixer.mixer())
|
||||||
sys.stdout.write("Capabilities: %s %s\n" % (' '.join(mixer.volumecap()),
|
print("Capabilities: %s %s" % (' '.join(mixer.volumecap()),
|
||||||
' '.join(mixer.switchcap())))
|
' '.join(mixer.switchcap())))
|
||||||
volumes = mixer.getvolume()
|
volumes = mixer.getvolume()
|
||||||
for i in range(len(volumes)):
|
for i in range(len(volumes)):
|
||||||
sys.stdout.write("Channel %i volume: %i%%\n" % (i,volumes[i]))
|
print("Channel %i volume: %i%%" % (i,volumes[i]))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mutes = mixer.getmute()
|
mutes = mixer.getmute()
|
||||||
for i in range(len(mutes)):
|
for i in range(len(mutes)):
|
||||||
if mutes[i]:
|
if mutes[i]:
|
||||||
sys.stdout.write("Channel %i is muted\n" % i)
|
print("Channel %i is muted" % i)
|
||||||
except alsaaudio.ALSAAudioError:
|
except alsaaudio.ALSAAudioError:
|
||||||
# May not support muting
|
# May not support muting
|
||||||
pass
|
pass
|
||||||
@@ -59,15 +55,15 @@ def show_mixer(name, idx=0):
|
|||||||
recs = mixer.getrec()
|
recs = mixer.getrec()
|
||||||
for i in range(len(recs)):
|
for i in range(len(recs)):
|
||||||
if recs[i]:
|
if recs[i]:
|
||||||
sys.stdout.write("Channel %i is recording\n" % i)
|
print("Channel %i is recording" % i)
|
||||||
except alsaaudio.ALSAAudioError:
|
except alsaaudio.ALSAAudioError:
|
||||||
# May not support recording
|
# May not support recording
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_mixer(name, args, idx=0):
|
def set_mixer(name, args, kwargs):
|
||||||
# Demonstrates how to set mixer settings
|
# Demonstrates how to set mixer settings
|
||||||
try:
|
try:
|
||||||
mixer = alsaaudio.Mixer(name, cardindex=idx)
|
mixer = alsaaudio.Mixer(name, **kwargs)
|
||||||
except alsaaudio.ALSAAudioError:
|
except alsaaudio.ALSAAudioError:
|
||||||
sys.stderr.write("No such mixer")
|
sys.stderr.write("No such mixer")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -106,17 +102,19 @@ def usage():
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
cardindex = 0
|
kwargs = {}
|
||||||
opts, args = getopt.getopt(sys.argv[1:], 'c:?h')
|
opts, args = getopt.getopt(sys.argv[1:], 'c:d:?h')
|
||||||
for o, a in opts:
|
for o, a in opts:
|
||||||
if o == '-c':
|
if o == '-c':
|
||||||
cardindex = int(a)
|
kwargs = { 'cardindex': int(a) }
|
||||||
|
elif o == '-d':
|
||||||
|
kwargs = { 'device': a }
|
||||||
else:
|
else:
|
||||||
usage()
|
usage()
|
||||||
|
|
||||||
if not len(args):
|
if not len(args):
|
||||||
list_mixers(cardindex)
|
list_mixers(kwargs)
|
||||||
elif len(args) == 1:
|
elif len(args) == 1:
|
||||||
show_mixer(args[0], cardindex)
|
show_mixer(args[0], kwargs)
|
||||||
else:
|
else:
|
||||||
set_mixer(args[0], args[1], cardindex)
|
set_mixer(args[0], args[1], kwargs)
|
||||||
|
|||||||
@@ -42,13 +42,18 @@ class MixerTest(unittest.TestCase):
|
|||||||
"""Test Mixer objects"""
|
"""Test Mixer objects"""
|
||||||
|
|
||||||
def testMixer(self):
|
def testMixer(self):
|
||||||
"""Open a Mixer on every card"""
|
"""Open the default Mixers and the Mixers on every card"""
|
||||||
|
|
||||||
|
for d in ['default'] + range(len(alsaaudio.cards())):
|
||||||
|
if type(d) == type(0):
|
||||||
|
kwargs = { 'cardindex': d }
|
||||||
|
else:
|
||||||
|
kwargs = { 'device': d }
|
||||||
|
|
||||||
# Mixers are addressed by index, not name
|
mixers = alsaaudio.mixers(**kwargs)
|
||||||
for i in range(len(alsaaudio.cards())):
|
|
||||||
mixers = alsaaudio.mixers(i)
|
|
||||||
for m in mixers:
|
for m in mixers:
|
||||||
mixer = alsaaudio.Mixer(m, cardindex=i)
|
mixer = alsaaudio.Mixer(m, **kwargs)
|
||||||
mixer.close()
|
mixer.close()
|
||||||
|
|
||||||
def testMixerAll(self):
|
def testMixerAll(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user