forked from auracaster/pyalsaaudio
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
279760add5 | ||
|
|
59a712c486 | ||
|
|
dfda54642d | ||
|
|
3f6fb9844d | ||
|
|
4d9f6e5b50 | ||
|
|
40a4a36b1d | ||
|
|
38ea69bbaa | ||
|
|
c8f3916337 | ||
|
|
f19af8eba0 | ||
|
|
b8980d992b | ||
|
|
ebd2b5359d | ||
|
|
c5f22fd7e0 | ||
|
|
3c3f0af74a | ||
|
|
17f3b440cc | ||
|
|
b2a303121a | ||
|
|
3168833b4e | ||
|
|
c74669850b | ||
|
|
1a4c0541d7 | ||
|
|
e6a6445375 | ||
|
|
97f2abcb30 | ||
|
|
a53ffd0d4f | ||
|
|
da71e01f9c | ||
|
|
f6736ec43a | ||
|
|
e48b294b84 | ||
|
|
d037297632 | ||
|
|
c8e7261e94 | ||
|
|
5c481b4094 | ||
|
|
1e3c7f3fd0 | ||
|
|
0ae60f80f3 | ||
|
|
4018ab4f6c | ||
|
|
07f84a8e95 | ||
|
|
d83e829de1 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,4 +8,7 @@ doc/_build/
|
||||
gh-pages/
|
||||
build/
|
||||
dist/
|
||||
.vscode/
|
||||
.vscode/
|
||||
/__pycache__/
|
||||
/pyalsaaudio.egg-info/
|
||||
*.raw
|
||||
|
||||
@@ -1,99 +1,103 @@
|
||||
Version 0.8.6:
|
||||
- Added four methods to the 'PCM' class to allow users to get detailed information about the device:
|
||||
# Version 0.9.2
|
||||
- Fix alsamixer_getvolume (#112 from @stephensp)
|
||||
|
||||
- 'getformats()' returns a dictionary of name / value pairs, one for each of the card's
|
||||
supported formats - e.g. '{"U8": 1, "S16_LE": 2}',
|
||||
- 'getchannels()' returns a list of the supported channel numbers, e.g. '[1, 2]',
|
||||
- 'getrates()' returns supported sample rates for the device, e.g. '[48000]',
|
||||
- 'getratebounds()' returns the device's official minimum and maximum supported
|
||||
sample rates as a tuple, e.g. '(4000, 48000)'.
|
||||
# Version 0.9.1:
|
||||
- Support decibel, percentage, and raw volumes in getvolume, setvolume, and getrange (#109 from @chrisdiamand)
|
||||
|
||||
# Version 0.9.0:
|
||||
- Added keyword arguments for channels, format, rate and periodsize
|
||||
- Deprecated `setchannel`, `setformat`, `setrate` and `setperiodsize`
|
||||
|
||||
# Version 0.8.6:
|
||||
- Added four methods to the `PCM` class to allow users to get detailed information about the device:
|
||||
|
||||
- `getformats()` returns a dictionary of name / value pairs, one for each of the card's
|
||||
supported formats - e.g. `{"U8": 1, "S16_LE": 2}`,
|
||||
- `getchannels()` returns a list of the supported channel numbers, e.g. `[1, 2]`,
|
||||
- `getrates()` returns supported sample rates for the device, e.g. `[48000]`,
|
||||
- `getratebounds()` returns the device's official minimum and maximum supported
|
||||
sample rates as a tuple, e.g. `(4000, 48000)`.
|
||||
|
||||
(#82 contributed by @jdstmporter)
|
||||
|
||||
- Prevent hang on close after capturing audio (#80 contributed by @daym)
|
||||
|
||||
Version 0.8.5:
|
||||
- Return an empty string/bytestring when 'read()' detects an
|
||||
# Version 0.8.5:
|
||||
- Return an empty string/bytestring when `read()` detects an
|
||||
overrun. Previously the returned data was undefined (contributed by @jcea)
|
||||
|
||||
- Unlimited setperiod buffer size when reading frames (contributed by @jcea)
|
||||
|
||||
Version 0.8.4:
|
||||
# Version 0.8.4:
|
||||
- Fix Python3 API usage broken in 0.8.3
|
||||
|
||||
Version 0.8.3:
|
||||
# Version 0.8.3:
|
||||
- Add DSD sample formats (contributed by @lintweaker)
|
||||
- Add Mixer.handleevents() to acknowledge events identified by select.poll (contributed by @PaulSD)
|
||||
- Add functions for listing cards and their names (contributed by @chrisdiamand)
|
||||
- Add a method for setting enums (contributed by @chrisdiamand)
|
||||
|
||||
Version 0.8.2:
|
||||
# Version 0.8.2:
|
||||
- fix #3 (we cannot get the revision from git for pip installs)
|
||||
|
||||
Version 0.8.1:
|
||||
# Version 0.8.1:
|
||||
- document changes (this file)
|
||||
|
||||
Version 0.8:
|
||||
- 'PCM()' has new 'device' and 'cardindex' keyword arguments.
|
||||
# Version 0.8:
|
||||
- `PCM()` has new `device` and `cardindex` keyword arguments.
|
||||
|
||||
The keyword 'device' allows to select virtual devices, 'cardindex' can be
|
||||
used to select hardware cards by index (as with 'mixers()' and 'Mixer()').
|
||||
The keyword `device` allows to select virtual devices, `cardindex` can be
|
||||
used to select hardware cards by index (as with `mixers()` and `Mixer()`).
|
||||
|
||||
The 'card' keyword argument is still supported, but deprecated.
|
||||
The `card` keyword argument is still supported, but deprecated.
|
||||
|
||||
The reason for this change is that the 'card' keyword argument guessed
|
||||
The reason for this change is that the `card` keyword argument guessed
|
||||
a device name from the card name, but this only works sometimes, and breaks
|
||||
opening virtual devices.
|
||||
|
||||
- new function 'pcms()' to list available PCM devices.
|
||||
- new function `pcms()` to list available PCM devices.
|
||||
|
||||
- mixers() and Mixer() take an additional 'device' keyword argument.
|
||||
- `mixers()` and `Mixer()` take an additional `device` keyword argument.
|
||||
This allows to list or open virtual devices.
|
||||
|
||||
- The default behaviour of Mixer() without any arguments has changed.
|
||||
Now Mixer() will try to open the 'default' Mixer instead of the Mixer
|
||||
- The default behaviour of `Mixer()` without any arguments has changed.
|
||||
Now Mixer() will try to open the `default` Mixer instead of the Mixer
|
||||
that is associated with card 0.
|
||||
|
||||
- fix a memory leak under Python 3.x
|
||||
|
||||
- some more memory leaks in error conditions fixed.
|
||||
|
||||
Version 0.7:
|
||||
# Version 0.7:
|
||||
- fixed several memory leaks (patch 3372909), contributed by Erik Kulyk)
|
||||
|
||||
|
||||
Version 0.6:
|
||||
# Version 0.6:
|
||||
- mostly reverted patch 2594366: alsapcm_setup did not do complete error
|
||||
checking for good reasons; some ALSA functions in alsapcm_setup may fail without
|
||||
rendering the device unusable
|
||||
|
||||
|
||||
Version 0.5:
|
||||
# Version 0.5:
|
||||
- applied patch 2777035: Fixed setrec method in alsaaudio.c
|
||||
This included a mixertest with more features
|
||||
- fixed/applied patch 2594366: alsapcm_setup does not do any error checking
|
||||
|
||||
|
||||
Version 0.4:
|
||||
# Version 0.4:
|
||||
- API changes: mixers() and Mixer() now take a card index instead of a
|
||||
card name as optional parameter.
|
||||
- Support for Python 3.0
|
||||
- Documentation converted to reStructuredText; use Sphinx instead of LaTeX.
|
||||
- added cards()
|
||||
- added PCM.close()
|
||||
- added Mixer.close()
|
||||
- added mixer.getenum()
|
||||
- added `cards()`
|
||||
- added `PCM.close()`
|
||||
- added `Mixer.close()`
|
||||
- added `mixer.getenum()`
|
||||
|
||||
|
||||
Version 0.3:
|
||||
- wrapped blocking calls with Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS
|
||||
# Version 0.3:
|
||||
- wrapped blocking calls with `Py_BEGIN_ALLOW_THREADS`/`Py_END_ALLOW_THREADS`
|
||||
- added pause
|
||||
|
||||
|
||||
Version 0.2:
|
||||
# Version 0.2:
|
||||
- Many bugfixes related to playback in particular
|
||||
- Module documentation in the doc subdirectory
|
||||
|
||||
|
||||
Version 0.1:
|
||||
# Version 0.1:
|
||||
- Initial version
|
||||
11
NOTES.md
11
NOTES.md
@@ -1,11 +0,0 @@
|
||||
# Publishing the documentation
|
||||
|
||||
- Install Sphinx; `sudo pip install sphinx`
|
||||
- Clone gh-pages branch: `cd doc; git clone -b gh-pages git@github.com:larsimmisch/pyalsaaudio.git gh-pages`
|
||||
- `cd doc; make publish`
|
||||
|
||||
# Release procedure
|
||||
|
||||
- Update version number in setup.py
|
||||
- Create tag and push it, i.e. `git tag x.y.z; git push origin x.y.z`
|
||||
- `python setup.py sdist upload -r pypi`
|
||||
648
alsaaudio.c
648
alsaaudio.c
@@ -28,6 +28,7 @@
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <alsa/version.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a))
|
||||
static const snd_pcm_format_t ALSAFormats[] = {
|
||||
@@ -88,6 +89,12 @@ static const unsigned ALSARates[] = {
|
||||
192000
|
||||
};
|
||||
|
||||
typedef enum volume_units_t {
|
||||
VOLUME_UNITS_PERCENTAGE,
|
||||
VOLUME_UNITS_RAW,
|
||||
VOLUME_UNITS_DB,
|
||||
} volume_units_t;
|
||||
|
||||
PyDoc_STRVAR(alsaaudio_module_doc,
|
||||
"This modules provides support for the ALSA audio API.\n"
|
||||
"\n"
|
||||
@@ -129,10 +136,13 @@ typedef struct {
|
||||
unsigned int cchannels;
|
||||
|
||||
/* min and max values for playback and capture volumes */
|
||||
long pmin;
|
||||
long pmax;
|
||||
long cmin;
|
||||
long cmax;
|
||||
long pmin, pmax;
|
||||
long cmin, cmax;
|
||||
/* min and max values for playback and capture volumes, in dB * 100 as
|
||||
* reported by ALSA */
|
||||
long pmin_dB, pmax_dB;
|
||||
long cmin_dB, cmax_dB;
|
||||
|
||||
snd_mixer_t *handle;
|
||||
} alsamixer_t;
|
||||
|
||||
@@ -181,6 +191,16 @@ get_pcmtype(PyObject *obj)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool is_value_volume_unit(long unit)
|
||||
{
|
||||
if (unit == VOLUME_UNITS_PERCENTAGE ||
|
||||
unit == VOLUME_UNITS_RAW ||
|
||||
unit == VOLUME_UNITS_DB) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
alsacard_list(PyObject *self, PyObject *args)
|
||||
{
|
||||
@@ -306,7 +326,7 @@ Return the card name and long name for card 'card_index'.");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_list(PyObject *self, PyObject *args)
|
||||
alsapcm_list(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *pcmtypeobj = NULL;
|
||||
long pcmtype;
|
||||
@@ -316,8 +336,11 @@ alsapcm_list(PyObject *self, PyObject *args)
|
||||
char *name, *io;
|
||||
const char *filter;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"|O:pcms", &pcmtypeobj))
|
||||
char *kw[] = { "pcmtype", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:pcms", kw, &pcmtypeobj)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pcmtype = get_pcmtype(pcmtypeobj);
|
||||
if (pcmtype < 0) {
|
||||
@@ -602,9 +625,6 @@ alsapcm_dumpinfo(alsapcm_t *self, PyObject *args)
|
||||
val = snd_pcm_hw_params_get_sbits(hwparams);
|
||||
printf("significant bits = %d\n", val);
|
||||
|
||||
snd_pcm_hw_params_get_period_time(hwparams, &val, &dir);
|
||||
printf("period time = %d us\n", val);
|
||||
|
||||
val = snd_pcm_hw_params_is_batch(hwparams);
|
||||
printf("is batch = %d\n", val);
|
||||
|
||||
@@ -639,6 +659,391 @@ alsapcm_dumpinfo(alsapcm_t *self, PyObject *args)
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_info(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
PyObject *info;
|
||||
PyObject *value;
|
||||
|
||||
unsigned int val,val2;
|
||||
snd_pcm_format_t fmt;
|
||||
int dir;
|
||||
snd_pcm_uframes_t frames;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
snd_pcm_hw_params_current(self->handle,hwparams);
|
||||
|
||||
if (!PyArg_ParseTuple(args,":info"))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle) {
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = PyDict_New();
|
||||
|
||||
value=PyUnicode_FromString(snd_pcm_name(self->handle));
|
||||
PyDict_SetItemString(info,"name",value);
|
||||
Py_DECREF(value);
|
||||
|
||||
value=PyUnicode_FromString(snd_pcm_state_name(snd_pcm_state(self->handle)));
|
||||
PyDict_SetItemString(info,"state",value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
|
||||
value=PyUnicode_FromString(snd_pcm_access_name((snd_pcm_access_t)val));
|
||||
PyDict_SetItemString(info,"access_type",value);
|
||||
Py_DECREF(value);
|
||||
|
||||
value = PyLong_FromUnsignedLong((unsigned long) self->pcmtype);
|
||||
PyDict_SetItemString(info," (call value) type",value);
|
||||
Py_DECREF(value);
|
||||
value=PyUnicode_FromString(snd_pcm_stream_name((snd_pcm_stream_t) self->pcmtype));
|
||||
PyDict_SetItemString(info," (call value) type_name",value);
|
||||
Py_DECREF(value);
|
||||
|
||||
value = PyLong_FromUnsignedLong((unsigned long) self->pcmmode);
|
||||
PyDict_SetItemString(info," (call value) mode",value);
|
||||
Py_DECREF(value);
|
||||
switch(self->pcmmode){
|
||||
case 0:
|
||||
value = PyUnicode_FromString("PCM_NORMAL");
|
||||
break;
|
||||
case SND_PCM_NONBLOCK:
|
||||
value = PyUnicode_FromString("PCM_NONBLOCK");
|
||||
break;
|
||||
case SND_PCM_ASYNC:
|
||||
value = PyUnicode_FromString("PCM_ASYNC");
|
||||
break;
|
||||
}
|
||||
PyDict_SetItemString(info," (call value) mode_name",value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_format(hwparams, &fmt);
|
||||
value=PyLong_FromUnsignedLong((unsigned long)fmt);
|
||||
PyDict_SetItemString(info,"format",value);
|
||||
Py_DECREF(value);
|
||||
value=PyUnicode_FromString(snd_pcm_format_name(fmt));
|
||||
PyDict_SetItemString(info,"format_name",value);
|
||||
Py_DECREF(value);
|
||||
value=PyUnicode_FromString(snd_pcm_format_description(fmt));
|
||||
PyDict_SetItemString(info,"format_description",value);
|
||||
Py_DECREF(value);
|
||||
|
||||
|
||||
snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
|
||||
value=PyUnicode_FromString(snd_pcm_subformat_name((snd_pcm_subformat_t)val));
|
||||
PyDict_SetItemString(info,"subformat_name",value);
|
||||
Py_DECREF(value);
|
||||
value=PyUnicode_FromString(snd_pcm_subformat_description((snd_pcm_subformat_t)val));
|
||||
PyDict_SetItemString(info,"subformat_description",value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_channels(hwparams, &val);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"channels", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_rate(hwparams, &val, &dir);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"rate", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_period_time(hwparams, &val, &dir);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"period_time", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_period_size(hwparams, &frames, &dir);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) frames);
|
||||
PyDict_SetItemString(info,"period_size", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_buffer_time(hwparams, &val, &dir);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"buffer_time", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_buffer_size(hwparams, (snd_pcm_uframes_t *) &val);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"buffer_size", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_periods(hwparams, &val, &dir);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"get_periods", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
snd_pcm_hw_params_get_rate_numden(hwparams, &val, &val2);
|
||||
value=PyTuple_Pack(2,PyLong_FromUnsignedLong((unsigned long) val) \
|
||||
,PyLong_FromUnsignedLong((unsigned long) val2));
|
||||
PyDict_SetItemString(info,"rate_numden", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_get_sbits(hwparams);
|
||||
value=PyLong_FromUnsignedLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"significant_bits", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_is_batch(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"is_batch", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_is_block_transfer(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"is_block_transfer", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_is_double(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"is_double", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_is_half_duplex(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"is_half_duplex", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_is_joint_duplex(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"is_joint_duplex", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_can_overrange(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"can_overrange", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_can_mmap_sample_resolution(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"can_mmap_sample_resolution", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_can_pause(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"can_pause", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_can_resume(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"can_resume", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
val = snd_pcm_hw_params_can_sync_start(hwparams);
|
||||
value=PyBool_FromLong((unsigned long) val);
|
||||
PyDict_SetItemString(info,"can_sync_start", value);
|
||||
Py_DECREF(value);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(pcm_info_doc,
|
||||
"info() -> dict\n\
|
||||
\n\
|
||||
Returns a dictionary with the alsa device parameters as it is realized. \n\
|
||||
Keys are retrieved from the alsa library if they can be accessed, if not \n\
|
||||
they represent values stored by pyalsaaudio and they are prefixed with ' (call value) '. \n\
|
||||
");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsa_asoundlib_version(PyObject * module, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args,":asoundlib_version"))
|
||||
return NULL;
|
||||
|
||||
return PyUnicode_FromString(snd_asoundlib_version());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(asoundlib_version_doc,
|
||||
"asoundlib_version() -> string\n\
|
||||
\n\
|
||||
Returns ALSA version string. \n\
|
||||
");
|
||||
|
||||
static PyObject *
|
||||
alsapcm_htimestamp(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
snd_htimestamp_t tstamp;
|
||||
snd_pcm_uframes_t avail;
|
||||
PyObject *result = NULL;
|
||||
|
||||
snd_pcm_htimestamp(self->handle , &avail, &tstamp);
|
||||
|
||||
result = PyTuple_New(3);
|
||||
PyTuple_SetItem(result, 0, PyLong_FromLongLong(tstamp.tv_sec));
|
||||
PyTuple_SetItem(result, 1, PyLong_FromLong(tstamp.tv_nsec));
|
||||
PyTuple_SetItem(result, 2, PyLong_FromLong(avail));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(htimestamp_doc,
|
||||
"htimestamp() -> tuple\n\
|
||||
\n\
|
||||
Returns a tuple containing the seconds since epoch in the first element \n\
|
||||
, nanoseconds in the second element, and number of frames available in \n\
|
||||
the buffer at the time of the time stamp. \n");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_set_tstamp_mode(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
snd_pcm_tstamp_t mode = SND_PCM_TSTAMP_ENABLE;
|
||||
int err;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"|i:set_tstamp_mode", &mode))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_t* swParams;
|
||||
snd_pcm_sw_params_alloca( &swParams);
|
||||
|
||||
snd_pcm_sw_params_current(self->handle, swParams);
|
||||
|
||||
snd_pcm_sw_params_set_tstamp_mode(self->handle, swParams, mode);
|
||||
|
||||
err = snd_pcm_sw_params(self->handle, swParams);
|
||||
|
||||
if (err < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unable to set pcm tstamp mode!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(set_tstamp_mode_doc,
|
||||
"set_tstamp_mode() -> None\n\
|
||||
\n\
|
||||
Set the timestamp mode of the device. \n");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_get_tstamp_mode(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
snd_pcm_tstamp_t mode;
|
||||
int err;
|
||||
|
||||
if (!PyArg_ParseTuple(args,":get_tstamp_mode"))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_t* swParams;
|
||||
snd_pcm_sw_params_alloca( &swParams);
|
||||
|
||||
snd_pcm_sw_params_current(self->handle, swParams);
|
||||
|
||||
err = snd_pcm_sw_params_get_tstamp_mode(swParams, &mode);
|
||||
|
||||
if (err < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unable to get pcm tstamp mode!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyLong_FromUnsignedLong((unsigned long) mode);
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(get_tstamp_mode_doc,
|
||||
"get_tstamp_mode() -> integer \n\
|
||||
\n\
|
||||
Get the timestamp mode of the device. \n");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsapcm_set_tstamp_type(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
snd_pcm_tstamp_type_t type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
|
||||
int err;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"|i:set_tstamp_type", &type))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_t* swParams;
|
||||
snd_pcm_sw_params_alloca( &swParams);
|
||||
|
||||
snd_pcm_sw_params_current(self->handle, swParams);
|
||||
|
||||
snd_pcm_sw_params_set_tstamp_type(self->handle, swParams, type);
|
||||
|
||||
err = snd_pcm_sw_params(self->handle, swParams);
|
||||
|
||||
if (err < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unable to set pcm tstamp type!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(set_tstamp_type_doc,
|
||||
"set_tstamp_type() -> None\n\
|
||||
\n\
|
||||
Set the timestamp type of the device. \n");
|
||||
|
||||
static PyObject *
|
||||
alsapcm_get_tstamp_type(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
snd_pcm_tstamp_type_t type;
|
||||
int err;
|
||||
|
||||
if (!PyArg_ParseTuple(args,":get_tstamp_type"))
|
||||
return NULL;
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "PCM device is closed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_t* swParams;
|
||||
snd_pcm_sw_params_alloca( &swParams);
|
||||
|
||||
snd_pcm_sw_params_current(self->handle, swParams);
|
||||
|
||||
err = snd_pcm_sw_params_get_tstamp_type(swParams, &type);
|
||||
|
||||
if (err < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unable to get pcm tstamp type!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyLong_FromUnsignedLong((unsigned long) type);
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(get_tstamp_type_doc,
|
||||
"get_tstamp_type() -> int \n\
|
||||
\n\
|
||||
Get the timestamp type of the device. \n");
|
||||
|
||||
|
||||
// auxiliary function
|
||||
|
||||
|
||||
@@ -1051,6 +1456,7 @@ alsapcm_read(alsapcm_t *self, PyObject *args)
|
||||
{
|
||||
int res;
|
||||
int size = self->framesize * self->periodsize;
|
||||
int sizeout = 0;
|
||||
PyObject *buffer_obj, *tuple_obj, *res_obj;
|
||||
char *buffer;
|
||||
|
||||
@@ -1105,18 +1511,22 @@ alsapcm_read(alsapcm_t *self, PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
if (res <= 0) {
|
||||
if (res > 0 ) {
|
||||
sizeout = res * self->framesize;
|
||||
}
|
||||
|
||||
if (size != sizeout) {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
/* If the following fails, it will free the object */
|
||||
if (_PyString_Resize(&buffer_obj, 0))
|
||||
if (_PyString_Resize(&buffer_obj, sizeout))
|
||||
return NULL;
|
||||
#else
|
||||
/* If the following fails, it will free the object */
|
||||
if (_PyBytes_Resize(&buffer_obj, 0))
|
||||
if (_PyBytes_Resize(&buffer_obj, sizeout))
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
res_obj = PyLong_FromLong(res);
|
||||
if (!res_obj) {
|
||||
Py_DECREF(buffer_obj);
|
||||
@@ -1345,7 +1755,7 @@ alsapcm_polldescriptors(alsapcm_t *self, PyObject *args)
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
PyList_SetItem(result, i,
|
||||
Py_BuildValue("II", fds[i].fd, fds[i].events));
|
||||
Py_BuildValue("ih", fds[i].fd, fds[i].events));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1370,7 +1780,13 @@ static PyMethodDef alsapcm_methods[] = {
|
||||
{"setformat", (PyCFunction)alsapcm_setformat, METH_VARARGS, setformat_doc},
|
||||
{"setperiodsize", (PyCFunction)alsapcm_setperiodsize, METH_VARARGS,
|
||||
setperiodsize_doc},
|
||||
{"htimestamp", (PyCFunction) alsapcm_htimestamp, METH_VARARGS, htimestamp_doc},
|
||||
{"set_tstamp_type", (PyCFunction) alsapcm_set_tstamp_type, METH_VARARGS, set_tstamp_type_doc},
|
||||
{"set_tstamp_mode", (PyCFunction) alsapcm_set_tstamp_mode, METH_VARARGS, set_tstamp_mode_doc},
|
||||
{"get_tstamp_type", (PyCFunction) alsapcm_get_tstamp_type, METH_VARARGS, get_tstamp_type_doc},
|
||||
{"get_tstamp_mode", (PyCFunction) alsapcm_get_tstamp_mode, METH_VARARGS, get_tstamp_mode_doc},
|
||||
{"dumpinfo", (PyCFunction)alsapcm_dumpinfo, METH_VARARGS},
|
||||
{"info", (PyCFunction)alsapcm_info, METH_VARARGS, pcm_info_doc},
|
||||
{"getformats", (PyCFunction)alsapcm_getformats, METH_VARARGS, getformats_doc},
|
||||
{"getratebounds", (PyCFunction)alsapcm_getratemaxmin, METH_VARARGS, getratebounds_doc},
|
||||
{"getrates", (PyCFunction)alsapcm_getrates, METH_VARARGS, getrates_doc},
|
||||
@@ -1385,6 +1801,12 @@ static PyMethodDef alsapcm_methods[] = {
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static PyMethodDef alsa_methods[] = {
|
||||
{"asoundlib_version", (PyCFunction) alsa_asoundlib_version, METH_VARARGS, asoundlib_version_doc},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
#if PY_VERSION_HEX < 0x02020000
|
||||
static PyObject *
|
||||
alsapcm_getattr(alsapcm_t *self, char *name) {
|
||||
@@ -1680,6 +2102,8 @@ alsamixer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
}
|
||||
snd_mixer_selem_get_playback_volume_range(elem, &self->pmin, &self->pmax);
|
||||
snd_mixer_selem_get_capture_volume_range(elem, &self->cmin, &self->cmax);
|
||||
snd_mixer_selem_get_playback_dB_range(elem, &self->pmin_dB, &self->pmax_dB);
|
||||
snd_mixer_selem_get_capture_dB_range(elem, &self->cmin_dB, &self->cmax_dB);
|
||||
|
||||
return (PyObject *)self;
|
||||
}
|
||||
@@ -1951,18 +2375,22 @@ static long alsamixer_getphysvolume(long min, long max, int percentage)
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
alsamixer_getvolume(alsamixer_t *self, PyObject *args)
|
||||
alsamixer_getvolume(alsamixer_t *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
snd_mixer_elem_t *elem;
|
||||
int channel;
|
||||
long ival;
|
||||
PyObject *pcmtypeobj = NULL;
|
||||
long pcmtype;
|
||||
int iunits = VOLUME_UNITS_PERCENTAGE;
|
||||
PyObject *result;
|
||||
PyObject *item;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"|O:getvolume", &pcmtypeobj))
|
||||
char *kw[] = { "pcmtype", "units", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:getvolume", kw, &pcmtypeobj, &iunits)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
@@ -1975,28 +2403,65 @@ alsamixer_getvolume(alsamixer_t *self, PyObject *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!is_value_volume_unit(iunits)) {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid volume units");
|
||||
return NULL;
|
||||
}
|
||||
volume_units_t units = iunits;
|
||||
|
||||
elem = alsamixer_find_elem(self->handle,self->controlname,self->controlid);
|
||||
|
||||
if (!pcmtypeobj || (pcmtypeobj == Py_None)) {
|
||||
if (self->pchannels) {
|
||||
pcmtype = SND_PCM_STREAM_PLAYBACK;
|
||||
}
|
||||
else {
|
||||
pcmtype = SND_PCM_STREAM_CAPTURE;
|
||||
}
|
||||
}
|
||||
|
||||
result = PyList_New(0);
|
||||
|
||||
for (channel = 0; channel <= SND_MIXER_SCHN_LAST; channel++) {
|
||||
if (pcmtype == SND_PCM_STREAM_PLAYBACK &&
|
||||
snd_mixer_selem_has_playback_channel(elem, channel))
|
||||
{
|
||||
snd_mixer_selem_get_playback_volume(elem, channel, &ival);
|
||||
item = PyLong_FromLong(alsamixer_getpercentage(self->pmin,
|
||||
self->pmax,
|
||||
ival));
|
||||
switch (units)
|
||||
{
|
||||
case VOLUME_UNITS_PERCENTAGE:
|
||||
snd_mixer_selem_get_playback_volume(elem, channel, &ival);
|
||||
ival = alsamixer_getpercentage(self->pmin, self->pmax, ival);
|
||||
break;
|
||||
case VOLUME_UNITS_RAW:
|
||||
snd_mixer_selem_get_playback_volume(elem, channel, &ival);
|
||||
break;
|
||||
case VOLUME_UNITS_DB:
|
||||
snd_mixer_selem_get_playback_dB(elem, channel, &ival);
|
||||
break;
|
||||
}
|
||||
|
||||
item = PyLong_FromLong(ival);
|
||||
PyList_Append(result, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
else if (pcmtype == SND_PCM_STREAM_CAPTURE
|
||||
&& snd_mixer_selem_has_capture_channel(elem, channel)
|
||||
&& snd_mixer_selem_has_capture_volume(elem)) {
|
||||
snd_mixer_selem_get_capture_volume(elem, channel, &ival);
|
||||
item = PyLong_FromLong(alsamixer_getpercentage(self->cmin,
|
||||
self->cmax,
|
||||
ival));
|
||||
switch (units)
|
||||
{
|
||||
case VOLUME_UNITS_PERCENTAGE:
|
||||
snd_mixer_selem_get_capture_volume(elem, channel, &ival);
|
||||
ival = alsamixer_getpercentage(self->cmin, self->cmax, ival);
|
||||
break;
|
||||
case VOLUME_UNITS_RAW:
|
||||
snd_mixer_selem_get_capture_volume(elem, channel, &ival);
|
||||
break;
|
||||
case VOLUME_UNITS_DB:
|
||||
snd_mixer_selem_get_capture_dB(elem, channel, &ival);
|
||||
break;
|
||||
}
|
||||
|
||||
item = PyLong_FromLong(ival);
|
||||
PyList_Append(result, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
@@ -2018,13 +2483,17 @@ if the mixer has this capability, otherwise PCM_CAPTURE");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsamixer_getrange(alsamixer_t *self, PyObject *args)
|
||||
alsamixer_getrange(alsamixer_t *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
snd_mixer_elem_t *elem;
|
||||
PyObject *pcmtypeobj = NULL;
|
||||
int iunits = VOLUME_UNITS_RAW;
|
||||
long pcmtype;
|
||||
long min = -1, max = -1;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"|O:getrange", &pcmtypeobj)) {
|
||||
char *kw[] = { "pcmtype", "units", NULL };
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:getrange", kw, &pcmtypeobj, &iunits)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2039,6 +2508,12 @@ alsamixer_getrange(alsamixer_t *self, PyObject *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!is_value_volume_unit(iunits)) {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid volume units");
|
||||
return NULL;
|
||||
}
|
||||
volume_units_t units = iunits;
|
||||
|
||||
elem = alsamixer_find_elem(self->handle, self->controlname,
|
||||
self->controlid);
|
||||
|
||||
@@ -2056,7 +2531,22 @@ alsamixer_getrange(alsamixer_t *self, PyObject *args)
|
||||
{
|
||||
if (snd_mixer_selem_has_playback_channel(elem, 0))
|
||||
{
|
||||
return Py_BuildValue("[ii]", self->pmin, self->pmax);
|
||||
switch (units)
|
||||
{
|
||||
case VOLUME_UNITS_PERCENTAGE:
|
||||
min = 0;
|
||||
max = 100;
|
||||
break;
|
||||
case VOLUME_UNITS_RAW:
|
||||
min = self->pmin;
|
||||
max = self->pmax;
|
||||
break;
|
||||
case VOLUME_UNITS_DB:
|
||||
min = self->pmin_dB;
|
||||
max = self->pmax_dB;
|
||||
break;
|
||||
}
|
||||
return Py_BuildValue("[ii]", min, max);
|
||||
}
|
||||
|
||||
PyErr_Format(ALSAAudioError, "Mixer %s,%d has no playback channel [%s]",
|
||||
@@ -2067,7 +2557,22 @@ alsamixer_getrange(alsamixer_t *self, PyObject *args)
|
||||
{
|
||||
if (snd_mixer_selem_has_capture_channel(elem, 0)
|
||||
&& snd_mixer_selem_has_capture_volume(elem)) {
|
||||
return Py_BuildValue("[ii]", self->cmin, self->cmax);
|
||||
switch (units)
|
||||
{
|
||||
case VOLUME_UNITS_PERCENTAGE:
|
||||
min = 0;
|
||||
max = 100;
|
||||
break;
|
||||
case VOLUME_UNITS_RAW:
|
||||
min = self->cmin;
|
||||
max = self->cmax;
|
||||
break;
|
||||
case VOLUME_UNITS_DB:
|
||||
min = self->cmin_dB;
|
||||
max = self->cmax_dB;
|
||||
break;
|
||||
}
|
||||
return Py_BuildValue("[ii]", min, max);
|
||||
}
|
||||
|
||||
PyErr_Format(ALSAAudioError, "Mixer %s,%d has no capture channel "
|
||||
@@ -2086,7 +2591,7 @@ PyDoc_STRVAR(getrange_doc,
|
||||
\n\
|
||||
Returns a list of tuples with the volume range (ints).\n\
|
||||
\n\
|
||||
The optional 'direction' argument can be either PCM_PLAYBACK or\n\
|
||||
The optional 'pcmtype' argument can be either PCM_PLAYBACK or\n\
|
||||
PCM_CAPTURE, which is relevant if the mixer can control both\n\
|
||||
playback and capture volume. The default value is 'playback'\n\
|
||||
if the mixer has this capability, otherwise 'capture'");
|
||||
@@ -2340,7 +2845,7 @@ This method will fail if the mixer has no capture switch capabilities.");
|
||||
|
||||
|
||||
static PyObject *
|
||||
alsamixer_setvolume(alsamixer_t *self, PyObject *args)
|
||||
alsamixer_setvolume(alsamixer_t *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
snd_mixer_elem_t *elem;
|
||||
int i;
|
||||
@@ -2348,16 +2853,14 @@ alsamixer_setvolume(alsamixer_t *self, PyObject *args)
|
||||
int physvolume;
|
||||
PyObject *pcmtypeobj = NULL;
|
||||
long pcmtype;
|
||||
int iunits = VOLUME_UNITS_PERCENTAGE;
|
||||
int channel = MIXER_CHANNEL_ALL;
|
||||
int done = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args,"l|iO:setvolume", &volume, &channel,
|
||||
&pcmtypeobj))
|
||||
return NULL;
|
||||
char *kw[] = { "volume", "channel", "pcmtype", "units", NULL };
|
||||
|
||||
if (volume < 0 || volume > 100)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "Volume must be between 0 and 100");
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|iOi:setvolume", kw, &volume,
|
||||
&channel, &pcmtypeobj, &iunits)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2366,6 +2869,18 @@ alsamixer_setvolume(alsamixer_t *self, PyObject *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!is_value_volume_unit(iunits)) {
|
||||
PyErr_SetString(ALSAAudioError, "Invalid volume units");
|
||||
return NULL;
|
||||
}
|
||||
volume_units_t units = iunits;
|
||||
|
||||
if (units == VOLUME_UNITS_PERCENTAGE && (volume < 0 || volume > 100))
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "Volume out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!self->handle)
|
||||
{
|
||||
PyErr_SetString(ALSAAudioError, "Mixer is closed");
|
||||
@@ -2388,18 +2903,40 @@ alsamixer_setvolume(alsamixer_t *self, PyObject *args)
|
||||
{
|
||||
if (pcmtype == SND_PCM_STREAM_PLAYBACK &&
|
||||
snd_mixer_selem_has_playback_channel(elem, i)) {
|
||||
physvolume = alsamixer_getphysvolume(self->pmin,
|
||||
self->pmax, volume);
|
||||
snd_mixer_selem_set_playback_volume(elem, i, physvolume);
|
||||
switch (units)
|
||||
{
|
||||
case VOLUME_UNITS_PERCENTAGE:
|
||||
physvolume = alsamixer_getphysvolume(self->pmin,
|
||||
self->pmax, volume);
|
||||
snd_mixer_selem_set_playback_volume(elem, i, physvolume);
|
||||
break;
|
||||
case VOLUME_UNITS_RAW:
|
||||
snd_mixer_selem_set_playback_volume(elem, i, volume);
|
||||
break;
|
||||
case VOLUME_UNITS_DB:
|
||||
snd_mixer_selem_set_playback_dB(elem, i, volume, 0);
|
||||
break;
|
||||
}
|
||||
done++;
|
||||
}
|
||||
else if (pcmtype == SND_PCM_STREAM_CAPTURE
|
||||
&& snd_mixer_selem_has_capture_channel(elem, i)
|
||||
&& snd_mixer_selem_has_capture_volume(elem))
|
||||
{
|
||||
physvolume = alsamixer_getphysvolume(self->cmin, self->cmax,
|
||||
volume);
|
||||
snd_mixer_selem_set_capture_volume(elem, i, physvolume);
|
||||
switch (units)
|
||||
{
|
||||
case VOLUME_UNITS_PERCENTAGE:
|
||||
physvolume = alsamixer_getphysvolume(self->cmin, self->cmax,
|
||||
volume);
|
||||
snd_mixer_selem_set_capture_volume(elem, i, physvolume);
|
||||
break;
|
||||
case VOLUME_UNITS_RAW:
|
||||
snd_mixer_selem_set_capture_volume(elem, i, volume);
|
||||
break;
|
||||
case VOLUME_UNITS_DB:
|
||||
snd_mixer_selem_set_capture_dB(elem, i, volume, 0);
|
||||
break;
|
||||
}
|
||||
done++;
|
||||
}
|
||||
}
|
||||
@@ -2425,7 +2962,7 @@ If the optional argument channel is present, the volume is set only for\n\
|
||||
this channel. This assumes that the mixer can control the volume for the\n\
|
||||
channels independently.\n\
|
||||
\n\
|
||||
The optional direction argument can be either PCM_PLAYBACK or PCM_CAPTURE.\n\
|
||||
The optional 'pcmtype' argument can be either PCM_PLAYBACK or PCM_CAPTURE.\n\
|
||||
It is relevant if the mixer has independent playback and capture volume\n\
|
||||
capabilities, and controls which of the volumes will be changed.\n\
|
||||
The default is 'playback' if the mixer has this capability, otherwise\n\
|
||||
@@ -2593,7 +3130,7 @@ alsamixer_polldescriptors(alsamixer_t *self, PyObject *args)
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
PyList_SetItem(result, i,
|
||||
Py_BuildValue("II", fds[i].fd, fds[i].events));
|
||||
Py_BuildValue("ih", fds[i].fd, fds[i].events));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -2647,13 +3184,13 @@ static PyMethodDef alsamixer_methods[] = {
|
||||
switchcap_doc},
|
||||
{"volumecap", (PyCFunction)alsamixer_volumecap, METH_VARARGS,
|
||||
volumecap_doc},
|
||||
{"getvolume", (PyCFunction)alsamixer_getvolume, METH_VARARGS,
|
||||
{"getvolume", (PyCFunction)alsamixer_getvolume, METH_VARARGS | METH_KEYWORDS,
|
||||
getvolume_doc},
|
||||
{"getrange", (PyCFunction)alsamixer_getrange, METH_VARARGS, getrange_doc},
|
||||
{"getrange", (PyCFunction)alsamixer_getrange, METH_VARARGS | METH_KEYWORDS, getrange_doc},
|
||||
{"getenum", (PyCFunction)alsamixer_getenum, METH_VARARGS, getenum_doc},
|
||||
{"getmute", (PyCFunction)alsamixer_getmute, METH_VARARGS, getmute_doc},
|
||||
{"getrec", (PyCFunction)alsamixer_getrec, METH_VARARGS, getrec_doc},
|
||||
{"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS,
|
||||
{"setvolume", (PyCFunction)alsamixer_setvolume, METH_VARARGS | METH_KEYWORDS,
|
||||
setvolume_doc},
|
||||
{"setenum", (PyCFunction)alsamixer_setenum, METH_VARARGS, setenum_doc},
|
||||
{"setmute", (PyCFunction)alsamixer_setmute, METH_VARARGS, setmute_doc},
|
||||
@@ -2729,7 +3266,7 @@ static PyMethodDef alsaaudio_methods[] = {
|
||||
{ "card_indexes", (PyCFunction)alsacard_list_indexes, METH_VARARGS, card_indexes_doc},
|
||||
{ "card_name", (PyCFunction)alsacard_name, METH_VARARGS, card_name_doc},
|
||||
{ "cards", (PyCFunction)alsacard_list, METH_VARARGS, cards_doc},
|
||||
{ "pcms", (PyCFunction)alsapcm_list, METH_VARARGS, pcms_doc},
|
||||
{ "pcms", (PyCFunction)alsapcm_list, METH_VARARGS|METH_KEYWORDS, pcms_doc},
|
||||
{ "mixers", (PyCFunction)alsamixer_list, METH_VARARGS|METH_KEYWORDS, mixers_doc},
|
||||
{ 0, 0 },
|
||||
};
|
||||
@@ -2803,6 +3340,8 @@ PyObject *PyInit_alsaaudio(void)
|
||||
Py_INCREF(ALSAAudioError);
|
||||
PyModule_AddObject(m, "ALSAAudioError", ALSAAudioError);
|
||||
|
||||
PyModule_AddFunctions(m, alsa_methods);
|
||||
|
||||
_EXPORT_INT(m, "PCM_PLAYBACK",SND_PCM_STREAM_PLAYBACK);
|
||||
_EXPORT_INT(m, "PCM_CAPTURE",SND_PCM_STREAM_CAPTURE);
|
||||
|
||||
@@ -2839,6 +3378,15 @@ PyObject *PyInit_alsaaudio(void)
|
||||
_EXPORT_INT(m, "PCM_FORMAT_U24_3LE",SND_PCM_FORMAT_U24_3LE);
|
||||
_EXPORT_INT(m, "PCM_FORMAT_U24_3BE",SND_PCM_FORMAT_U24_3BE);
|
||||
|
||||
/* PCM tstamp modes */
|
||||
_EXPORT_INT(m, "PCM_TSTAMP_NONE",SND_PCM_TSTAMP_NONE);
|
||||
_EXPORT_INT(m, "PCM_TSTAMP_ENABLE",SND_PCM_TSTAMP_ENABLE);
|
||||
|
||||
/* PCM tstamp types */
|
||||
_EXPORT_INT(m, "PCM_TSTAMP_TYPE_GETTIMEOFDAY",SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY);
|
||||
_EXPORT_INT(m, "PCM_TSTAMP_TYPE_MONOTONIC",SND_PCM_TSTAMP_TYPE_MONOTONIC);
|
||||
_EXPORT_INT(m, "PCM_TSTAMP_TYPE_MONOTONIC_RAW",SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW);
|
||||
|
||||
/* DSD sample formats are included in ALSA 1.0.29 and higher
|
||||
* define OVERRIDE_DSD_COMPILE to include DSD sample support
|
||||
* if you use a patched ALSA lib version
|
||||
@@ -2868,6 +3416,10 @@ PyObject *PyInit_alsaaudio(void)
|
||||
_EXPORT_INT(m, "MIXER_SCHN_MONO", SND_MIXER_SCHN_MONO);
|
||||
#endif
|
||||
|
||||
_EXPORT_INT(m, "VOLUME_UNITS_PERCENTAGE", VOLUME_UNITS_PERCENTAGE)
|
||||
_EXPORT_INT(m, "VOLUME_UNITS_RAW", VOLUME_UNITS_RAW)
|
||||
_EXPORT_INT(m, "VOLUME_UNITS_DB", VOLUME_UNITS_DB)
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return m;
|
||||
#endif
|
||||
|
||||
@@ -11,10 +11,14 @@ Create and push a tag naming the version (i.e. 0.9.0):
|
||||
git tag 0.9.0
|
||||
git push origin 0.9.0
|
||||
|
||||
Upload the package:
|
||||
Create the package:
|
||||
|
||||
python3 setup.py sdist
|
||||
|
||||
Upload the package
|
||||
|
||||
twine upload dist/*
|
||||
|
||||
Don't forget to update the documentation.
|
||||
|
||||
# Publish the documentation
|
||||
|
||||
@@ -33,13 +33,13 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
||||
.. % should be enclosed in \var{...}.
|
||||
|
||||
|
||||
.. function:: pcms([type=PCM_PLAYBACK])
|
||||
.. function:: pcms(pcmtype=PCM_PLAYBACK)
|
||||
|
||||
List available PCM devices by name.
|
||||
|
||||
Arguments are:
|
||||
|
||||
* *type* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`
|
||||
* *pcmtype* - can be either :const:`PCM_CAPTURE` or :const:`PCM_PLAYBACK`
|
||||
(default).
|
||||
|
||||
**Note:**
|
||||
@@ -97,6 +97,9 @@ The :mod:`alsaaudio` module defines functions and classes for using ALSA.
|
||||
changed. Since 0.8, this functions returns the mixers for the default
|
||||
device, not the mixers for the first card.
|
||||
|
||||
.. function:: asoundlib_version()
|
||||
|
||||
Return a Python string containing the ALSA version found.
|
||||
|
||||
.. _pcm-objects:
|
||||
|
||||
@@ -116,8 +119,7 @@ following arguments:
|
||||
(default).
|
||||
* *mode* - can be either :const:`PCM_NONBLOCK`, or :const:`PCM_NORMAL`
|
||||
(default).
|
||||
* *rate* - the sampling rate in Hz. Typical values are ``8000``
|
||||
(mainly used for telephony), ``16000``, ``44100`` (default), ``48000`` and ``96000``.
|
||||
* *rate* - the sampling rate in Hz. Typical values are ``8000`` (mainly used for telephony), ``16000``, ``44100`` (default), ``48000`` and ``96000``.
|
||||
* *channels* - the number of channels. The default value is 2 (stereo).
|
||||
* *format* - the data format. This controls how the PCM device interprets data for playback, and how data is encoded in captures.
|
||||
The default value is :const:`PCM_FORMAT_S16_LE`.
|
||||
@@ -256,11 +258,61 @@ PCM objects have the following methods:
|
||||
.. method:: PCM.polldescriptors()
|
||||
|
||||
Returns a tuple of *(file descriptor, eventmask)* that can be used to
|
||||
wait for changes on the mixer with *select.poll*.
|
||||
wait for changes on the PCM with *select.poll*.
|
||||
|
||||
The *eventmask* value is compatible with `poll.register`__ in the Python
|
||||
:const:`select` module.
|
||||
|
||||
.. method:: PCM.set_tstamp_mode([mode=PCM_TSTAMP_ENABLE])
|
||||
|
||||
Set the ALSA timestamp mode on the device. The mode argument can be set to
|
||||
either :const:`PCM_TSTAMP_NONE` or :const:`PCM_TSTAMP_ENABLE`.
|
||||
|
||||
.. method:: PCM.get_tstamp_mode()
|
||||
|
||||
Return the integer value corresponding to the ALSA timestamp mode. The
|
||||
return value can be either :const:`PCM_TSTAMP_NONE` or :const:`PCM_TSTAMP_ENABLE`.
|
||||
|
||||
.. method:: PCM.set_tstamp_type([type=PCM_TSTAMP_TYPE_GETTIMEOFDAY])
|
||||
|
||||
Set the ALSA timestamp mode on the device. The type argument
|
||||
can be set to either :const:`PCM_TSTAMP_TYPE_GETTIMEOFDAY`,
|
||||
:const:`PCM_TSTAMP_TYPE_MONOTONIC` or :const:`PCM_TSTAMP_TYPE_MONOTONIC_RAW`.
|
||||
|
||||
.. method:: PCM.get_tstamp_type()
|
||||
|
||||
Return the integer value corresponding to the ALSA timestamp type. The
|
||||
return value can be either :const:`PCM_TSTAMP_TYPE_GETTIMEOFDAY`,
|
||||
:const:`PCM_TSTAMP_TYPE_MONOTONIC` or :const:`PCM_TSTAMP_TYPE_MONOTONIC_RAW`.
|
||||
|
||||
.. method:: PCM.htimestamp()
|
||||
|
||||
Return a Python tuple *(seconds, nanoseconds, frames_available_in_buffer)*.
|
||||
|
||||
The type of output is controlled by the tstamp_type, as described in the table below.
|
||||
|
||||
================================= ===========================================
|
||||
Timestamp Type Description
|
||||
================================= ===========================================
|
||||
``PCM_TSTAMP_TYPE_GETTIMEOFDAY`` System-wide realtime clock with seconds
|
||||
since epoch.
|
||||
``PCM_TSTAMP_TYPE_MONOTONIC`` Monotonic time from an unspecified starting
|
||||
time. Progress is NTP synchronized.
|
||||
``PCM_TSTAMP_TYPE_MONOTONIC_RAW`` Monotonic time from an unspecified starting
|
||||
time using only the system clock.
|
||||
================================= ===========================================
|
||||
|
||||
The timestamp mode is controlled by the tstamp_mode, as described in the table below.
|
||||
|
||||
================================= ===========================================
|
||||
Timestamp Mode Description
|
||||
================================= ===========================================
|
||||
``PCM_TSTAMP_NONE`` No timestamp.
|
||||
``PCM_TSTAMP_ENABLE`` Update timestamp at every hardware position
|
||||
update.
|
||||
================================= ===========================================
|
||||
|
||||
|
||||
__ poll_objects_
|
||||
|
||||
**A few hints on using PCM devices for playback**
|
||||
@@ -414,11 +466,11 @@ Mixer objects have the following methods:
|
||||
This method will fail if the mixer has no playback switch capabilities.
|
||||
|
||||
|
||||
.. method:: Mixer.getrange([direction])
|
||||
.. method:: Mixer.getrange(pcmtype=PCM_PLAYBACK)
|
||||
|
||||
Return the volume range of the ALSA mixer controlled by this object.
|
||||
|
||||
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or
|
||||
The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or
|
||||
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both
|
||||
playback and capture volume. The default value is :const:`PCM_PLAYBACK`
|
||||
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
|
||||
@@ -432,18 +484,18 @@ Mixer objects have the following methods:
|
||||
This method will fail if the mixer has no capture switch capabilities.
|
||||
|
||||
|
||||
.. method:: Mixer.getvolume([direction])
|
||||
.. method:: Mixer.getvolume(pcmtype=PCM_PLAYBACK)
|
||||
|
||||
Returns a list with the current volume settings for each channel. The list
|
||||
elements are integer percentages.
|
||||
|
||||
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or
|
||||
The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or
|
||||
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both
|
||||
playback and capture volume. The default value is :const:`PCM_PLAYBACK`
|
||||
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
|
||||
|
||||
|
||||
.. method:: Mixer.setvolume(volume, [channel], [direction])
|
||||
.. method:: Mixer.setvolume(volume, channel=None, pcmtype=PCM_PLAYBACK)
|
||||
|
||||
Change the current volume settings for this mixer. The *volume* argument
|
||||
controls the new volume setting as an integer percentage.
|
||||
@@ -452,7 +504,7 @@ Mixer objects have the following methods:
|
||||
only for this channel. This assumes that the mixer can control the
|
||||
volume for the channels independently.
|
||||
|
||||
The optional *direction* argument can be either :const:`PCM_PLAYBACK` or
|
||||
The optional *pcmtype* argument can be either :const:`PCM_PLAYBACK` or
|
||||
:const:`PCM_CAPTURE`, which is relevant if the mixer can control both
|
||||
playback and capture volume. The default value is :const:`PCM_PLAYBACK`
|
||||
if the mixer has playback channels, otherwise it is :const:`PCM_CAPTURE`.
|
||||
|
||||
29
mixertest.py
29
mixertest.py
@@ -43,11 +43,36 @@ def show_mixer(name, kwargs):
|
||||
sys.exit(1)
|
||||
|
||||
print("Mixer name: '%s'" % mixer.mixer())
|
||||
print("Capabilities: %s %s" % (' '.join(mixer.volumecap()),
|
||||
volcap = mixer.volumecap()
|
||||
print("Capabilities: %s %s" % (' '.join(volcap),
|
||||
' '.join(mixer.switchcap())))
|
||||
|
||||
if "Volume" in volcap or "Joined Volume" in volcap or "Playback Volume" in volcap:
|
||||
pmin, pmax = mixer.getrange(alsaaudio.PCM_PLAYBACK)
|
||||
pmin_keyword, pmax_keyword = mixer.getrange(pcmtype=alsaaudio.PCM_PLAYBACK, units=alsaaudio.VOLUME_UNITS_RAW)
|
||||
pmin_default, pmax_default = mixer.getrange()
|
||||
assert pmin == pmin_keyword
|
||||
assert pmax == pmax_keyword
|
||||
assert pmin == pmin_default
|
||||
assert pmax == pmax_default
|
||||
print("Raw playback volume range {}-{}".format(pmin, pmax))
|
||||
pmin_dB, pmax_dB = mixer.getrange(units=alsaaudio.VOLUME_UNITS_DB)
|
||||
print("dB playback volume range {}-{}".format(pmin_dB / 100.0, pmax_dB / 100.0))
|
||||
|
||||
if "Capture Volume" in volcap or "Joined Capture Volume" in volcap:
|
||||
# Check that `getrange` works with keyword and positional arguments
|
||||
cmin, cmax = mixer.getrange(alsaaudio.PCM_CAPTURE)
|
||||
cmin_keyword, cmax_keyword = mixer.getrange(pcmtype=alsaaudio.PCM_CAPTURE, units=alsaaudio.VOLUME_UNITS_RAW)
|
||||
assert cmin == cmin_keyword
|
||||
assert cmax == cmax_keyword
|
||||
print("Raw capture volume range {}-{}".format(cmin, cmax))
|
||||
cmin_dB, cmax_dB = mixer.getrange(pcmtype=alsaaudio.PCM_CAPTURE, units=alsaaudio.VOLUME_UNITS_DB)
|
||||
print("dB capture volume range {}-{}".format(cmin_dB / 100.0, cmax_dB / 100.0))
|
||||
|
||||
volumes = mixer.getvolume()
|
||||
volumes_dB = mixer.getvolume(units=alsaaudio.VOLUME_UNITS_DB)
|
||||
for i in range(len(volumes)):
|
||||
print("Channel %i volume: %i%%" % (i,volumes[i]))
|
||||
print("Channel %i volume: %i%% (%.1f dB)" % (i, volumes[i], volumes_dB[i] / 100.0))
|
||||
|
||||
try:
|
||||
mutes = mixer.getmute()
|
||||
|
||||
@@ -58,7 +58,7 @@ if __name__ == '__main__':
|
||||
loops -= 1
|
||||
# Read data from device
|
||||
l, data = inp.read()
|
||||
|
||||
|
||||
if l:
|
||||
f.write(data)
|
||||
time.sleep(.001)
|
||||
|
||||
Reference in New Issue
Block a user