Add Mac OS X Host API Stream Info interface.
git-svn-id: https://svn.csail.mit.edu/pyaudio/branches/pyaudio-0.2.0@16 b6966296-9315-0410-889a-8473bed30898
This commit is contained in:
parent
3ab6d8241a
commit
7a93fffa0a
3 changed files with 384 additions and 11 deletions
|
@ -33,6 +33,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include "portaudio.h"
|
||||
#include "_portaudiomodule.h"
|
||||
|
||||
#ifdef MACOSX
|
||||
#include "pa_mac_core.h"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_FRAMES_PER_BUFFER 1024
|
||||
/* #define VERBOSE */
|
||||
|
||||
|
@ -668,6 +672,236 @@ _create_paHostApiInfo_object(void)
|
|||
return obj;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Host-Specific Objects
|
||||
*************************************************************/
|
||||
|
||||
/*************************************************************
|
||||
* --> Mac OS X
|
||||
*************************************************************/
|
||||
|
||||
#ifdef MACOSX
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PaMacCoreStreamInfo *paMacCoreStreamInfo;
|
||||
int flags;
|
||||
SInt32 *channelMap;
|
||||
int channelMapSize;
|
||||
} _pyAudio_MacOSX_hostApiSpecificStreamInfo;
|
||||
|
||||
typedef _pyAudio_MacOSX_hostApiSpecificStreamInfo _pyAudio_Mac_HASSI;
|
||||
|
||||
static void
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(_pyAudio_Mac_HASSI *self) {
|
||||
|
||||
if (self->paMacCoreStreamInfo != NULL) {
|
||||
free( self->paMacCoreStreamInfo );
|
||||
self->paMacCoreStreamInfo = NULL;
|
||||
}
|
||||
|
||||
if (self->channelMap != NULL) {
|
||||
free( self->channelMap );
|
||||
}
|
||||
|
||||
self->flags = paMacCorePlayNice;
|
||||
self->channelMapSize = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_dealloc(_pyAudio_Mac_HASSI *self) {
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self);
|
||||
self->ob_type->tp_free( (PyObject *) self );
|
||||
}
|
||||
|
||||
static int
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_init(_pyAudio_Mac_HASSI *self,
|
||||
PyObject *args,
|
||||
PyObject *kwargs) {
|
||||
|
||||
PyObject *channel_map = NULL;
|
||||
int flags = paMacCorePlayNice;
|
||||
|
||||
static char *kwlist[] = {"flags", "channel_map", NULL};
|
||||
|
||||
if (! PyArg_ParseTupleAndKeywords(args, kwargs, "|iO", kwlist,
|
||||
&flags, &channel_map)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// cleanup (just in case)
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self);
|
||||
|
||||
if (channel_map != NULL) {
|
||||
// ensure channel_map is an array
|
||||
if (! PyTuple_Check(channel_map)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Channel map must be a tuple");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// generate SInt32 channelMap
|
||||
self->channelMapSize = (int) PyTuple_Size(channel_map);
|
||||
|
||||
self->channelMap = (SInt32 *) malloc(sizeof(SInt32) * self->channelMapSize);
|
||||
|
||||
if (self->channelMap == NULL) {
|
||||
PyErr_SetString(PyExc_SystemError, "Out of memory");
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *element;
|
||||
int i;
|
||||
for (i = 0; i < self->channelMapSize; ++i) {
|
||||
element = PyTuple_GetItem(channel_map, i);
|
||||
if (element == NULL) {
|
||||
// error condition
|
||||
PyErr_SetString(PyExc_ValueError, "Internal error: out of bounds index");
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// make sure element is an integer
|
||||
if (!PyInt_Check(element)) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Channel Map must consist of integer elements");
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// OK, looks good
|
||||
self->channelMap[i] = (SInt32) PyInt_AsLong(element);
|
||||
}
|
||||
}
|
||||
|
||||
// malloc self->paMacCoreStreamInfo
|
||||
self->paMacCoreStreamInfo = (PaMacCoreStreamInfo *) malloc(sizeof(PaMacCoreStreamInfo));
|
||||
|
||||
if (self->paMacCoreStreamInfo == NULL) {
|
||||
PyErr_SetString(PyExc_SystemError, "Out of memeory");
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_cleanup(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PaMacCore_SetupStreamInfo(self->paMacCoreStreamInfo, flags);
|
||||
|
||||
if (self->channelMap) {
|
||||
PaMacCore_SetupChannelMap(self->paMacCoreStreamInfo,
|
||||
self->channelMap,
|
||||
self->channelMapSize);
|
||||
}
|
||||
|
||||
self->flags = flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_get_flags(_pyAudio_Mac_HASSI *self,
|
||||
void *closure) {
|
||||
return PyInt_FromLong(self->flags);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_get_channel_map(_pyAudio_Mac_HASSI *self,
|
||||
void *closure) {
|
||||
if (self->channelMap == NULL || self->channelMapSize == 0) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
int i;
|
||||
PyObject *channelMapTuple = PyTuple_New(self->channelMapSize);
|
||||
for (i = 0; i < self->channelMapSize; ++i) {
|
||||
PyObject *element = PyInt_FromLong(self->channelMap[i]);
|
||||
if (!element) {
|
||||
PyErr_SetString(PyExc_SystemError, "Invalid channel map");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (PyTuple_SetItem(channelMapTuple,
|
||||
i,
|
||||
PyInt_FromLong(self->channelMap[i]))){
|
||||
// non-zero on error
|
||||
PyErr_SetString(PyExc_SystemError, "Can't create channel map.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return channelMapTuple;
|
||||
}
|
||||
|
||||
static int
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_antiset(_pyAudio_Mac_HASSI *self,
|
||||
PyObject *value,
|
||||
void *closure)
|
||||
{
|
||||
/* read-only: do not allow users to change values */
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"Fields read-only: cannot modify values");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static PyGetSetDef _pyAudio_MacOSX_hostApiSpecificStreamInfo_getseters[] = {
|
||||
{"flags",
|
||||
(getter) _pyAudio_MacOSX_hostApiSpecificStreamInfo_get_flags,
|
||||
(setter) _pyAudio_MacOSX_hostApiSpecificStreamInfo_antiset,
|
||||
"flags",
|
||||
NULL},
|
||||
|
||||
{"channel_map",
|
||||
(getter) _pyAudio_MacOSX_hostApiSpecificStreamInfo_get_channel_map,
|
||||
(setter) _pyAudio_MacOSX_hostApiSpecificStreamInfo_antiset,
|
||||
"channel map",
|
||||
NULL},
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyTypeObject _pyAudio_MacOSX_hostApiSpecificStreamInfoType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0, /*ob_size*/
|
||||
"_portaudio.PaMacCoreStreamInfo", /*tp_name*/
|
||||
sizeof(_pyAudio_MacOSX_hostApiSpecificStreamInfo), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
(destructor) _pyAudio_MacOSX_hostApiSpecificStreamInfo_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
0, /*tp_call*/
|
||||
0, /*tp_str*/
|
||||
0, /*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||||
"Mac OS X Specific HostAPI configuration", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_getseters, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
0, /* tp_new */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Stream Wrapper Python Object
|
||||
|
@ -1262,6 +1496,15 @@ pa_open(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
PaSampleFormat format;
|
||||
PaError err;
|
||||
|
||||
#ifdef MACOSX
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo *inputHostSpecificStreamInfo = NULL;
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfo *outputHostSpecificStreamInfo = NULL;
|
||||
#else
|
||||
/* mostly ignored...*/
|
||||
PyObject *inputHostSpecificStreamInfo = NULL;
|
||||
PyObject *outputHostSpecificStreamInfo = NULL;
|
||||
#endif
|
||||
|
||||
/* default to neither output nor input */
|
||||
input = 0;
|
||||
output = 0;
|
||||
|
@ -1277,15 +1520,31 @@ pa_open(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
"input_device_index",
|
||||
"output_device_index",
|
||||
"frames_per_buffer",
|
||||
"input_host_api_specific_stream_info",
|
||||
"output_host_api_specific_stream_info",
|
||||
NULL};
|
||||
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii|iiOOi", kwlist,
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
#ifdef MACOSX
|
||||
"iii|iiOOiO!O!",
|
||||
#else
|
||||
"iii|iiOOiOO",
|
||||
#endif
|
||||
kwlist,
|
||||
&rate, &channels, &format,
|
||||
&input, &output,
|
||||
&input_device_index_arg,
|
||||
&output_device_index_arg,
|
||||
&frames_per_buffer))
|
||||
&frames_per_buffer,
|
||||
#ifdef MACOSX
|
||||
&_pyAudio_MacOSX_hostApiSpecificStreamInfoType,
|
||||
#endif
|
||||
&inputHostSpecificStreamInfo,
|
||||
#ifdef MACOSX
|
||||
&_pyAudio_MacOSX_hostApiSpecificStreamInfoType,
|
||||
#endif
|
||||
&outputHostSpecificStreamInfo))
|
||||
|
||||
return NULL;
|
||||
|
||||
|
@ -1398,6 +1657,14 @@ pa_open(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
outputParameters->suggestedLatency =
|
||||
Pa_GetDeviceInfo( outputParameters->device )->defaultLowOutputLatency;
|
||||
outputParameters->hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
#ifdef MACOSX
|
||||
if (outputHostSpecificStreamInfo) {
|
||||
outputParameters->hostApiSpecificStreamInfo =
|
||||
outputHostSpecificStreamInfo->paMacCoreStreamInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if (input) {
|
||||
|
@ -1425,6 +1692,13 @@ pa_open(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
inputParameters->suggestedLatency =
|
||||
Pa_GetDeviceInfo( inputParameters->device )->defaultLowInputLatency;
|
||||
inputParameters->hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
#ifdef MACOSX
|
||||
if (inputHostSpecificStreamInfo) {
|
||||
inputParameters->hostApiSpecificStreamInfo =
|
||||
inputHostSpecificStreamInfo->paMacCoreStreamInfo;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PaStream *stream = NULL;
|
||||
|
@ -2104,12 +2378,24 @@ init_portaudio(void)
|
|||
if (PyType_Ready(&_pyAudio_paHostApiInfoType) < 0)
|
||||
return;
|
||||
|
||||
#ifdef MACOSX
|
||||
_pyAudio_MacOSX_hostApiSpecificStreamInfoType.tp_new = PyType_GenericNew;
|
||||
if (PyType_Ready(&_pyAudio_MacOSX_hostApiSpecificStreamInfoType) < 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
m = Py_InitModule("_portaudio", paMethods);
|
||||
|
||||
Py_INCREF(&_pyAudio_StreamType);
|
||||
Py_INCREF(&_pyAudio_paDeviceInfoType);
|
||||
Py_INCREF(&_pyAudio_paHostApiInfoType);
|
||||
|
||||
#ifdef MACOSX
|
||||
Py_INCREF(&_pyAudio_MacOSX_hostApiSpecificStreamInfoType);
|
||||
PyModule_AddObject(m, "paMacCoreStreamInfo",
|
||||
(PyObject *)&_pyAudio_MacOSX_hostApiSpecificStreamInfoType);
|
||||
#endif
|
||||
|
||||
/* Add PortAudio constants */
|
||||
|
||||
/* host apis */
|
||||
|
@ -2180,5 +2466,30 @@ init_portaudio(void)
|
|||
PyModule_AddIntConstant(m, "paIncompatibleStreamHostApi",
|
||||
paIncompatibleStreamHostApi);
|
||||
|
||||
#ifdef MACOSX
|
||||
PyModule_AddIntConstant(m, "paMacCoreChangeDeviceParameters",
|
||||
paMacCoreChangeDeviceParameters);
|
||||
PyModule_AddIntConstant(m, "paMacCoreFailIfConversionRequired",
|
||||
paMacCoreFailIfConversionRequired);
|
||||
PyModule_AddIntConstant(m, "paMacCoreConversionQualityMin",
|
||||
paMacCoreConversionQualityMin);
|
||||
PyModule_AddIntConstant(m, "MacCoreConversionQualityMedium",
|
||||
paMacCoreConversionQualityMedium);
|
||||
PyModule_AddIntConstant(m, "paMacCoreConversionQualityLow",
|
||||
paMacCoreConversionQualityLow);
|
||||
PyModule_AddIntConstant(m, "paMacCoreConversionQualityHigh",
|
||||
paMacCoreConversionQualityHigh);
|
||||
PyModule_AddIntConstant(m, "paMacCoreConversionQualityMax",
|
||||
paMacCoreConversionQualityMax);
|
||||
PyModule_AddIntConstant(m, "paMacCorePlayNice",
|
||||
paMacCorePlayNice);
|
||||
PyModule_AddIntConstant(m, "paMacCorePro",
|
||||
paMacCorePro);
|
||||
PyModule_AddIntConstant(m, "paMacCoreMinimizeCPUButPlayNice",
|
||||
paMacCoreMinimizeCPUButPlayNice);
|
||||
PyModule_AddIntConstant(m, "paMacCoreMinimizeCPU",
|
||||
paMacCoreMinimizeCPU);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -205,6 +205,10 @@ PaErrorCode = ['paNoError',
|
|||
'paCanNotWriteToAnInputOnlyStream',
|
||||
'paIncompatibleStreamHostApi']
|
||||
|
||||
# Mac OS X host specific stream information
|
||||
if sys.platform == "darwin":
|
||||
paMacCoreStreamInfo = pa.paMacCoreStreamInfo
|
||||
|
||||
############################################################
|
||||
# Convenience Functions
|
||||
############################################################
|
||||
|
@ -311,7 +315,9 @@ class Stream:
|
|||
input_device_index = None,
|
||||
output_device_index = None,
|
||||
frames_per_buffer = 1024,
|
||||
start = True):
|
||||
start = True,
|
||||
input_host_api_specific_stream_info = None,
|
||||
output_host_api_specific_stream_info = None):
|
||||
"""
|
||||
Initialize a stream; this should be called by
|
||||
`PyAudio.open`. A stream can either be input, output, or both.
|
||||
|
@ -364,15 +370,28 @@ class Stream:
|
|||
self._format = format
|
||||
self._frames_per_buffer = frames_per_buffer
|
||||
|
||||
arguments = {
|
||||
'rate' : rate,
|
||||
'channels' : channels,
|
||||
'format' : format,
|
||||
'input' : input,
|
||||
'output' : output,
|
||||
'input_device_index' : input_device_index,
|
||||
'output_device_index' : output_device_index,
|
||||
'frames_per_buffer' : frames_per_buffer}
|
||||
|
||||
if input_host_api_specific_stream_info:
|
||||
arguments[
|
||||
'input_host_api_specific_stream_info'
|
||||
] = input_host_api_specific_stream_info
|
||||
|
||||
if output_host_api_specific_stream_info:
|
||||
arguments[
|
||||
'output_host_api_specific_stream_info'
|
||||
] = output_host_api_specific_stream_info
|
||||
|
||||
# calling pa.open returns a stream object
|
||||
self._stream = pa.open(rate = rate,
|
||||
channels = channels,
|
||||
format = format,
|
||||
input = input,
|
||||
output = output,
|
||||
input_device_index = input_device_index,
|
||||
output_device_index = output_device_index,
|
||||
frames_per_buffer = frames_per_buffer)
|
||||
self._stream = pa.open(**arguments)
|
||||
|
||||
self._input_latency = self._stream.inputLatency
|
||||
self._output_latency = self._stream.outputLatency
|
||||
|
|
43
pyaudio/tests/play_wave_mac_channel_map.py
Normal file
43
pyaudio/tests/play_wave_mac_channel_map.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
""" PyAudio Example: Play a wave file """
|
||||
|
||||
import pyaudio
|
||||
import wave
|
||||
import sys
|
||||
|
||||
chunk = 1024
|
||||
|
||||
PyAudio = pyaudio.PyAudio
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print "Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0]
|
||||
sys.exit(-1)
|
||||
|
||||
wf = wave.open(sys.argv[1], 'rb')
|
||||
|
||||
p = PyAudio()
|
||||
|
||||
s = pyaudio.paMacCoreStreamInfo(channel_map = (0, 1))
|
||||
|
||||
# open stream
|
||||
stream = p.open(format =
|
||||
p.get_format_from_width(wf.getsampwidth()),
|
||||
channels = wf.getnchannels(),
|
||||
rate = wf.getframerate(),
|
||||
output = True,
|
||||
output_host_api_specific_stream_info = s)
|
||||
|
||||
# read data
|
||||
data = wf.readframes(chunk)
|
||||
|
||||
# play stream
|
||||
while data != '':
|
||||
stream.write(data)
|
||||
data = wf.readframes(chunk)
|
||||
|
||||
stream.stop_stream()
|
||||
stream.close()
|
||||
|
||||
p.terminate()
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue