添加一些关于midi播放的项目
This commit is contained in:
@@ -0,0 +1,563 @@
|
||||
package rtaudio
|
||||
|
||||
/*
|
||||
|
||||
#cgo CXXFLAGS: -g
|
||||
#cgo LDFLAGS: -lstdc++ -g
|
||||
|
||||
#cgo linux CXXFLAGS: -D__LINUX_ALSA__
|
||||
#cgo linux LDFLAGS: -lm -lasound -pthread
|
||||
|
||||
#cgo linux,pulseaudio CXXFLAGS: -D__LINUX_PULSE__
|
||||
#cgo linux,pulseaudio LDFLAGS: -lpulse -lpulse-simple
|
||||
|
||||
#cgo jack CXXFLAGS: -D__UNIX_JACK__
|
||||
#cgo jack LDFLAGS: -ljack
|
||||
|
||||
#cgo windows CXXFLAGS: -D__WINDOWS_WASAPI__
|
||||
#cgo windows LDFLAGS: -lm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -lwinmm -lole32 -static
|
||||
|
||||
#cgo darwin CXXFLAGS: -D__MACOSX_CORE__
|
||||
#cgo darwin LDFLAGS: -framework CoreAudio -framework CoreFoundation
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "rtaudio_stub.h"
|
||||
|
||||
extern int goCallback(void *out, void *in, unsigned int nFrames,
|
||||
double stream_time, rtaudio_stream_status_t status, void *userdata);
|
||||
|
||||
static inline void cgoRtAudioOpenStream(rtaudio_t audio,
|
||||
rtaudio_stream_parameters_t *output_params,
|
||||
rtaudio_stream_parameters_t *input_params,
|
||||
rtaudio_format_t format,
|
||||
unsigned int sample_rate,
|
||||
unsigned int *buffer_frames,
|
||||
int cb_id,
|
||||
rtaudio_stream_options_t *options) {
|
||||
rtaudio_open_stream(audio, output_params, input_params,
|
||||
format, sample_rate, buffer_frames,
|
||||
goCallback, (void *)(uintptr_t)cb_id, options, NULL);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// API is an enumeration of available compiled APIs. Supported API include
|
||||
// Alsa/PulseAudio/OSS, Jack, CoreAudio, WASAPI/ASIO/DS and dummy API.
|
||||
type API C.rtaudio_api_t
|
||||
|
||||
const (
|
||||
// APIUnspecified looks for a working compiled API.
|
||||
APIUnspecified API = C.RTAUDIO_API_UNSPECIFIED
|
||||
// APILinuxALSA uses the Advanced Linux Sound Architecture API.
|
||||
APILinuxALSA = C.RTAUDIO_API_LINUX_ALSA
|
||||
// APILinuxPulse uses the Linux PulseAudio API.
|
||||
APILinuxPulse = C.RTAUDIO_API_LINUX_PULSE
|
||||
// APILinuxOSS uses the Linux Open Sound System API.
|
||||
APILinuxOSS = C.RTAUDIO_API_LINUX_OSS
|
||||
// APIUnixJack uses the Jack Low-Latency Audio Server API.
|
||||
APIUnixJack = C.RTAUDIO_API_UNIX_JACK
|
||||
// APIMacOSXCore uses Macintosh OS-X Core Audio API.
|
||||
APIMacOSXCore = C.RTAUDIO_API_MACOSX_CORE
|
||||
// APIWindowsWASAPI uses the Microsoft WASAPI API.
|
||||
APIWindowsWASAPI = C.RTAUDIO_API_WINDOWS_WASAPI
|
||||
// APIWindowsASIO uses the Steinberg Audio Stream I/O API.
|
||||
APIWindowsASIO = C.RTAUDIO_API_WINDOWS_ASIO
|
||||
// APIWindowsDS uses the Microsoft DirectSound API.
|
||||
APIWindowsDS = C.RTAUDIO_API_WINDOWS_DS
|
||||
// APIDummy is a compilable but non-functional API.
|
||||
APIDummy = C.RTAUDIO_API_DUMMY
|
||||
)
|
||||
|
||||
func (api API) String() string {
|
||||
switch api {
|
||||
case APIUnspecified:
|
||||
return "unspecified"
|
||||
case APILinuxALSA:
|
||||
return "alsa"
|
||||
case APILinuxPulse:
|
||||
return "pulse"
|
||||
case APILinuxOSS:
|
||||
return "oss"
|
||||
case APIUnixJack:
|
||||
return "jack"
|
||||
case APIMacOSXCore:
|
||||
return "coreaudio"
|
||||
case APIWindowsWASAPI:
|
||||
return "wasapi"
|
||||
case APIWindowsASIO:
|
||||
return "asio"
|
||||
case APIWindowsDS:
|
||||
return "directsound"
|
||||
case APIDummy:
|
||||
return "dummy"
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
|
||||
// StreamStatus defines over- or underflow flags in the audio callback.
|
||||
type StreamStatus C.rtaudio_stream_status_t
|
||||
|
||||
const (
|
||||
// StatusInputOverflow indicates that data was discarded because of an
|
||||
// overflow condition at the driver.
|
||||
StatusInputOverflow StreamStatus = C.RTAUDIO_STATUS_INPUT_OVERFLOW
|
||||
// StatusOutputUnderflow indicates that the output buffer ran low, likely
|
||||
// producing a break in the output sound.
|
||||
StatusOutputUnderflow StreamStatus = C.RTAUDIO_STATUS_OUTPUT_UNDERFLOW
|
||||
)
|
||||
|
||||
// Version returns current RtAudio library version string.
|
||||
func Version() string {
|
||||
return C.GoString(C.rtaudio_version())
|
||||
}
|
||||
|
||||
// CompiledAPI determines the available compiled audio APIs.
|
||||
func CompiledAPI() (apis []API) {
|
||||
capis := (*[1 << 27]C.rtaudio_api_t)(unsafe.Pointer(C.rtaudio_compiled_api()))
|
||||
for i := 0; ; i++ {
|
||||
api := capis[i]
|
||||
if api == C.RTAUDIO_API_UNSPECIFIED {
|
||||
break
|
||||
}
|
||||
apis = append(apis, API(api))
|
||||
}
|
||||
return apis
|
||||
}
|
||||
|
||||
// DeviceInfo is the public device information structure for returning queried values.
|
||||
type DeviceInfo struct {
|
||||
Name string
|
||||
Probed bool
|
||||
NumOutputChannels int
|
||||
NumInputChannels int
|
||||
NumDuplexChannels int
|
||||
IsDefaultOutput bool
|
||||
IsDefaultInput bool
|
||||
|
||||
//rtaudio_format_t native_formats;
|
||||
|
||||
PreferredSampleRate uint
|
||||
SampleRates []int
|
||||
}
|
||||
|
||||
// StreamParams is the structure for specifying input or output stream parameters.
|
||||
type StreamParams struct {
|
||||
DeviceID uint
|
||||
NumChannels uint
|
||||
FirstChannel uint
|
||||
}
|
||||
|
||||
// StreamFlags is a set of RtAudio stream option flags.
|
||||
type StreamFlags C.rtaudio_stream_flags_t
|
||||
|
||||
const (
|
||||
// FlagsNoninterleaved is set to use non-interleaved buffers (default = interleaved).
|
||||
FlagsNoninterleaved = C.RTAUDIO_FLAGS_NONINTERLEAVED
|
||||
// FlagsMinimizeLatency when set attempts to configure stream parameters for lowest possible latency.
|
||||
FlagsMinimizeLatency = C.RTAUDIO_FLAGS_MINIMIZE_LATENCY
|
||||
// FlagsHogDevice when set attempts to grab device for exclusive use.
|
||||
FlagsHogDevice = C.RTAUDIO_FLAGS_HOG_DEVICE
|
||||
// FlagsScheduleRealtime is set in attempt to select realtime scheduling (round-robin) for the callback thread.
|
||||
FlagsScheduleRealtime = C.RTAUDIO_FLAGS_SCHEDULE_REALTIME
|
||||
// FlagsAlsaUseDefault is set to use the "default" PCM device (ALSA only).
|
||||
FlagsAlsaUseDefault = C.RTAUDIO_FLAGS_ALSA_USE_DEFAULT
|
||||
)
|
||||
|
||||
// StreamOptions is the structure for specifying stream options.
|
||||
type StreamOptions struct {
|
||||
Flags StreamFlags
|
||||
NumBuffers uint
|
||||
Priotity int
|
||||
Name string
|
||||
}
|
||||
|
||||
// RtAudio is a "controller" used to select an available audio i/o interface.
|
||||
type RtAudio interface {
|
||||
Destroy()
|
||||
CurrentAPI() API
|
||||
Devices() ([]DeviceInfo, error)
|
||||
DefaultOutputDevice() int
|
||||
DefaultInputDevice() int
|
||||
|
||||
Open(out, in *StreamParams, format Format, sampleRate uint, frames uint, cb Callback, opts *StreamOptions) error
|
||||
Close()
|
||||
Start() error
|
||||
Stop() error
|
||||
Abort() error
|
||||
|
||||
IsOpen() bool
|
||||
IsRunning() bool
|
||||
|
||||
Latency() (int, error)
|
||||
SampleRate() (uint, error)
|
||||
Time() (time.Duration, error)
|
||||
SetTime(time.Duration) error
|
||||
|
||||
ShowWarnings(bool)
|
||||
}
|
||||
|
||||
type rtaudio struct {
|
||||
audio C.rtaudio_t
|
||||
cb Callback
|
||||
inputChannels int
|
||||
outputChannels int
|
||||
format Format
|
||||
}
|
||||
|
||||
var _ RtAudio = &rtaudio{}
|
||||
|
||||
// Create a new RtAudio instance using the given API.
|
||||
func Create(api API) (RtAudio, error) {
|
||||
audio := C.rtaudio_create(C.rtaudio_api_t(api))
|
||||
if C.rtaudio_error(audio) != nil {
|
||||
return nil, errors.New(C.GoString(C.rtaudio_error(audio)))
|
||||
}
|
||||
return &rtaudio{audio: audio}, nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Destroy() {
|
||||
C.rtaudio_destroy(audio.audio)
|
||||
}
|
||||
|
||||
func (audio *rtaudio) CurrentAPI() API {
|
||||
return API(C.rtaudio_current_api(audio.audio))
|
||||
}
|
||||
|
||||
func (audio *rtaudio) DefaultInputDevice() int {
|
||||
return int(C.rtaudio_get_default_input_device(audio.audio))
|
||||
}
|
||||
|
||||
func (audio *rtaudio) DefaultOutputDevice() int {
|
||||
return int(C.rtaudio_get_default_output_device(audio.audio))
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Devices() ([]DeviceInfo, error) {
|
||||
n := C.rtaudio_device_count(audio.audio)
|
||||
devices := []DeviceInfo{}
|
||||
for i := C.int(0); i < n; i++ {
|
||||
cinfo := C.rtaudio_get_device_info(audio.audio, i)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return nil, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
sr := []int{}
|
||||
for _, r := range cinfo.sample_rates {
|
||||
if r == 0 {
|
||||
break
|
||||
}
|
||||
sr = append(sr, int(r))
|
||||
}
|
||||
devices = append(devices, DeviceInfo{
|
||||
Name: C.GoString(&cinfo.name[0]),
|
||||
Probed: cinfo.probed != 0,
|
||||
NumInputChannels: int(cinfo.input_channels),
|
||||
NumOutputChannels: int(cinfo.output_channels),
|
||||
NumDuplexChannels: int(cinfo.duplex_channels),
|
||||
IsDefaultOutput: cinfo.is_default_output != 0,
|
||||
IsDefaultInput: cinfo.is_default_input != 0,
|
||||
PreferredSampleRate: uint(cinfo.preferred_sample_rate),
|
||||
SampleRates: sr,
|
||||
})
|
||||
// TODO: formats
|
||||
}
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// Format defines RtAudio data format type.
|
||||
type Format int
|
||||
|
||||
const (
|
||||
// FormatInt8 uses 8-bit signed integer.
|
||||
FormatInt8 Format = C.RTAUDIO_FORMAT_SINT8
|
||||
// FormatInt16 uses 16-bit signed integer.
|
||||
FormatInt16 = C.RTAUDIO_FORMAT_SINT16
|
||||
// FormatInt24 uses 24-bit signed integer.
|
||||
FormatInt24 = C.RTAUDIO_FORMAT_SINT24
|
||||
// FormatInt32 uses 32-bit signed integer.
|
||||
FormatInt32 = C.RTAUDIO_FORMAT_SINT32
|
||||
// FormatFloat32 uses 32-bit floating point values normalized between (-1..1).
|
||||
FormatFloat32 = C.RTAUDIO_FORMAT_FLOAT32
|
||||
// FormatFloat64 uses 64-bit floating point values normalized between (-1..1).
|
||||
FormatFloat64 = C.RTAUDIO_FORMAT_FLOAT64
|
||||
)
|
||||
|
||||
// Buffer is a common interface for audio buffers of various data format types.
|
||||
type Buffer interface {
|
||||
Len() int
|
||||
Int8() []int8
|
||||
Int16() []int16
|
||||
Int24() []Int24
|
||||
Int32() []int32
|
||||
Float32() []float32
|
||||
Float64() []float64
|
||||
}
|
||||
|
||||
// Int24 is a helper type to convert int32 values to int24 and back.
|
||||
type Int24 [3]byte
|
||||
|
||||
// Set Int24 value using the least significant bytes of the given number n.
|
||||
func (i *Int24) Set(n int32) {
|
||||
(*i)[0], (*i)[1], (*i)[2] = byte(n&0xff), byte((n&0xff00)>>8), byte((n&0xff0000)>>16)
|
||||
}
|
||||
|
||||
// Get Int24 value as int32.
|
||||
func (i Int24) Get() int32 {
|
||||
n := int32(i[0]) | int32(i[1])<<8 | int32(i[2])<<16
|
||||
if n&0x800000 != 0 {
|
||||
n |= ^0xffffff
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
type buffer struct {
|
||||
format Format
|
||||
length int
|
||||
numChannels int
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func (b *buffer) Len() int {
|
||||
if b.ptr == nil {
|
||||
return 0
|
||||
}
|
||||
return b.length
|
||||
}
|
||||
|
||||
func (b *buffer) Int8() []int8 {
|
||||
if b.format != FormatInt8 {
|
||||
return nil
|
||||
}
|
||||
if b.ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 30]int8)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
||||
}
|
||||
|
||||
func (b *buffer) Int16() []int16 {
|
||||
if b.format != FormatInt16 {
|
||||
return nil
|
||||
}
|
||||
if b.ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 29]int16)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
||||
}
|
||||
|
||||
func (b *buffer) Int24() []Int24 {
|
||||
if b.format != FormatInt24 {
|
||||
return nil
|
||||
}
|
||||
if b.ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 28]Int24)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
||||
}
|
||||
|
||||
func (b *buffer) Int32() []int32 {
|
||||
if b.format != FormatInt32 {
|
||||
return nil
|
||||
}
|
||||
if b.ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 27]int32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
||||
}
|
||||
|
||||
func (b *buffer) Float32() []float32 {
|
||||
if b.format != FormatFloat32 {
|
||||
return nil
|
||||
}
|
||||
if b.ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 27]float32)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
||||
}
|
||||
|
||||
func (b *buffer) Float64() []float64 {
|
||||
if b.format != FormatFloat64 {
|
||||
return nil
|
||||
}
|
||||
if b.ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 23]float64)(b.ptr)[:b.length*b.numChannels : b.length*b.numChannels]
|
||||
}
|
||||
|
||||
// Callback is a client-defined function that will be invoked when input data
|
||||
// is available and/or output data is needed.
|
||||
type Callback func(out Buffer, in Buffer, dur time.Duration, status StreamStatus) int
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
audios = map[int]*rtaudio{}
|
||||
)
|
||||
|
||||
func registerAudio(a *rtaudio) int {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for i := 0; ; i++ {
|
||||
if _, ok := audios[i]; !ok {
|
||||
audios[i] = a
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unregisterAudio(a *rtaudio) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for i := 0; i < len(audios); i++ {
|
||||
if audios[i] == a {
|
||||
delete(audios, i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findAudio(k int) *rtaudio {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return audios[k]
|
||||
}
|
||||
|
||||
//export goCallback
|
||||
func goCallback(out, in unsafe.Pointer, frames C.uint, sec C.double,
|
||||
status C.rtaudio_stream_status_t, userdata unsafe.Pointer) C.int {
|
||||
|
||||
k := int(uintptr(userdata))
|
||||
audio := findAudio(k)
|
||||
dur := time.Duration(time.Microsecond * time.Duration(sec*1000000.0))
|
||||
inbuf := &buffer{audio.format, int(frames), audio.inputChannels, in}
|
||||
outbuf := &buffer{audio.format, int(frames), audio.outputChannels, out}
|
||||
return C.int(audio.cb(outbuf, inbuf, dur, StreamStatus(status)))
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Open(out, in *StreamParams, format Format, sampleRate uint,
|
||||
frames uint, cb Callback, opts *StreamOptions) error {
|
||||
var (
|
||||
cInPtr *C.rtaudio_stream_parameters_t
|
||||
cOutPtr *C.rtaudio_stream_parameters_t
|
||||
cOptsPtr *C.rtaudio_stream_options_t
|
||||
cIn C.rtaudio_stream_parameters_t
|
||||
cOut C.rtaudio_stream_parameters_t
|
||||
cOpts C.rtaudio_stream_options_t
|
||||
)
|
||||
|
||||
audio.inputChannels = 0
|
||||
audio.outputChannels = 0
|
||||
if out != nil {
|
||||
audio.outputChannels = int(out.NumChannels)
|
||||
cOut.device_id = C.uint(out.DeviceID)
|
||||
cOut.num_channels = C.uint(out.NumChannels)
|
||||
cOut.first_channel = C.uint(out.FirstChannel)
|
||||
cOutPtr = &cOut
|
||||
}
|
||||
if in != nil {
|
||||
audio.inputChannels = int(in.NumChannels)
|
||||
cIn.device_id = C.uint(in.DeviceID)
|
||||
cIn.num_channels = C.uint(in.NumChannels)
|
||||
cIn.first_channel = C.uint(in.FirstChannel)
|
||||
cInPtr = &cIn
|
||||
}
|
||||
if opts != nil {
|
||||
cOpts.flags = C.rtaudio_stream_flags_t(opts.Flags)
|
||||
cOpts.num_buffers = C.uint(opts.NumBuffers)
|
||||
cOpts.priority = C.int(opts.Priotity)
|
||||
cOptsPtr = &cOpts
|
||||
}
|
||||
framesCount := C.uint(frames)
|
||||
audio.format = format
|
||||
audio.cb = cb
|
||||
|
||||
k := registerAudio(audio)
|
||||
C.cgoRtAudioOpenStream(audio.audio, cOutPtr, cInPtr,
|
||||
C.rtaudio_format_t(format), C.uint(sampleRate), &framesCount, C.int(k), cOptsPtr)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Close() {
|
||||
unregisterAudio(audio)
|
||||
C.rtaudio_close_stream(audio.audio)
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Start() error {
|
||||
C.rtaudio_start_stream(audio.audio)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Stop() error {
|
||||
C.rtaudio_stop_stream(audio.audio)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Abort() error {
|
||||
C.rtaudio_abort_stream(audio.audio)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) IsOpen() bool {
|
||||
return C.rtaudio_is_stream_open(audio.audio) != 0
|
||||
}
|
||||
|
||||
func (audio *rtaudio) IsRunning() bool {
|
||||
return C.rtaudio_is_stream_running(audio.audio) != 0
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Latency() (int, error) {
|
||||
latency := C.rtaudio_get_stream_latency(audio.audio)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return int(latency), nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) SampleRate() (uint, error) {
|
||||
sampleRate := C.rtaudio_get_stream_sample_rate(audio.audio)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return uint(sampleRate), nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) Time() (time.Duration, error) {
|
||||
sec := C.rtaudio_get_stream_time(audio.audio)
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return 0, errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return time.Duration(time.Microsecond * time.Duration(sec*1000000.0)), nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) SetTime(t time.Duration) error {
|
||||
sec := float64(t) * 1000000.0 / float64(time.Microsecond)
|
||||
C.rtaudio_set_stream_time(audio.audio, C.double(sec))
|
||||
if C.rtaudio_error(audio.audio) != nil {
|
||||
return errors.New(C.GoString(C.rtaudio_error(audio.audio)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (audio *rtaudio) ShowWarnings(show bool) {
|
||||
if show {
|
||||
C.rtaudio_show_warnings(audio.audio, 1)
|
||||
} else {
|
||||
C.rtaudio_show_warnings(audio.audio, 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "../../../RtAudio.h"
|
||||
|
||||
#include "../../../RtAudio.cpp"
|
||||
#include "../../../rtaudio_c.cpp"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../../rtaudio_c.h"
|
||||
@@ -0,0 +1,71 @@
|
||||
package rtaudio
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleCompiledAPI() {
|
||||
log.Println("RtAudio version: ", Version())
|
||||
for _, api := range CompiledAPI() {
|
||||
log.Println("Compiled API: ", api)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRtAudio_Devices() {
|
||||
audio, err := Create(APIUnspecified)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer audio.Destroy()
|
||||
devices, err := audio.Devices()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, d := range devices {
|
||||
log.Printf("Audio device: %#v\n", d)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRtAudio_Open() {
|
||||
const (
|
||||
sampleRate = 44100
|
||||
bufSz = 512
|
||||
freq = 440.0
|
||||
)
|
||||
phase := 0.0
|
||||
audio, err := Create(APIUnspecified)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer audio.Destroy()
|
||||
|
||||
params := StreamParams{
|
||||
DeviceID: uint(audio.DefaultOutputDevice()),
|
||||
NumChannels: 2,
|
||||
FirstChannel: 0,
|
||||
}
|
||||
options := StreamOptions{
|
||||
Flags: FlagsAlsaUseDefault,
|
||||
}
|
||||
cb := func(out, in Buffer, dur time.Duration, status StreamStatus) int {
|
||||
samples := out.Float32()
|
||||
for i := 0; i < len(samples)/2; i++ {
|
||||
sample := float32(math.Sin(2 * math.Pi * phase))
|
||||
phase += freq / sampleRate
|
||||
|
||||
samples[i*2] = sample
|
||||
samples[i*2+1] = sample
|
||||
}
|
||||
return 0
|
||||
}
|
||||
err = audio.Open(¶ms, nil, FormatFloat32, sampleRate, bufSz, cb, &options)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer audio.Close()
|
||||
audio.Start()
|
||||
defer audio.Stop()
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
from __future__ import print_function
|
||||
import threading
|
||||
import rtaudio as rt
|
||||
|
||||
from math import cos
|
||||
|
||||
import struct
|
||||
|
||||
|
||||
class audio_generator:
|
||||
def __init__(self):
|
||||
self.idx = -1
|
||||
self.freq = 440.
|
||||
def __call__(self):
|
||||
self.idx += 1
|
||||
if self.idx%48000 == 0:
|
||||
self.freq *= 2**(1/12.)
|
||||
return 0.5*cos(2.*3.1416*self.freq*self.idx/48000.)
|
||||
|
||||
|
||||
class callback:
|
||||
def __init__(self, gen):
|
||||
self.gen = gen
|
||||
self.i = 0
|
||||
def __call__(self,playback, capture):
|
||||
[struct.pack_into("f", playback, 4*o, self.gen()) for o in range(256)]
|
||||
self.i = self.i + 256
|
||||
if self.i > 48000*10:
|
||||
print('.')
|
||||
return 1
|
||||
|
||||
try:
|
||||
# if we have numpy, replace the above class
|
||||
import numpy as np
|
||||
class callback:
|
||||
def __init__(self, gen):
|
||||
print('Using Numpy.')
|
||||
self.freq = 440.
|
||||
t = np.arange(256, dtype=np.float32) / 48000.0
|
||||
self.phase = 2*np.pi*t
|
||||
self.inc = 2*np.pi*256/48000
|
||||
self.k = 0
|
||||
def __call__(self, playback, capture):
|
||||
# Calculate sinusoid using numpy vector operation, as
|
||||
# opposed to per-sample computations in the generator
|
||||
# above that must be collected and packed one at a time.
|
||||
self.k += 256
|
||||
if self.k > 48000:
|
||||
self.freq *= 2**(1/12.)
|
||||
self.k = 0
|
||||
self.phase += self.inc
|
||||
samples = 0.5*np.cos(self.phase * self.freq)
|
||||
|
||||
# Ensure result is the right size!
|
||||
assert samples.shape[0] == 256
|
||||
assert samples.dtype == np.float32
|
||||
|
||||
# Use numpy array view to do a once-copy into memoryview
|
||||
# (ie. we only do a single byte-wise copy of the final
|
||||
# result into 'playback')
|
||||
usamples = samples.view(dtype=np.uint8)
|
||||
playback_array = np.array(playback, copy=False)
|
||||
np.copyto(playback_array, usamples)
|
||||
except ModuleNotFoundError:
|
||||
print('Numpy not available, using struct.')
|
||||
|
||||
dac = rt.RtAudio()
|
||||
|
||||
n = dac.getDeviceCount()
|
||||
print('Number of devices available: ', n)
|
||||
|
||||
for i in range(n):
|
||||
try:
|
||||
print(dac.getDeviceInfo(i))
|
||||
except rt.RtError as e:
|
||||
print(e)
|
||||
|
||||
|
||||
print('Default output device: ', dac.getDefaultOutputDevice())
|
||||
print('Default input device: ', dac.getDefaultInputDevice())
|
||||
|
||||
print('is stream open: ', dac.isStreamOpen())
|
||||
print('is stream running: ', dac.isStreamRunning())
|
||||
|
||||
oParams = {'deviceId': 0, 'nChannels': 1, 'firstChannel': 0}
|
||||
iParams = {'deviceId': 0, 'nChannels': 1, 'firstChannel': 0}
|
||||
|
||||
try:
|
||||
dac.openStream(oParams,oParams,48000,256,callback(audio_generator()) )
|
||||
except rt.RtError as e:
|
||||
print(e)
|
||||
else:
|
||||
dac.startStream()
|
||||
|
||||
import time
|
||||
print('latency: ', dac.getStreamLatency())
|
||||
|
||||
while (dac.isStreamRunning()):
|
||||
time.sleep(0.1)
|
||||
|
||||
print(dac.getStreamTime())
|
||||
|
||||
dac.stopStream()
|
||||
dac.abortStream()
|
||||
dac.closeStream()
|
||||
@@ -0,0 +1,57 @@
|
||||
PyRtAudio - a python wrapper around RtAudio that allows to perform audio i/o operations in real-time from the python language.
|
||||
|
||||
By Antoine Lefebvre, 2011
|
||||
|
||||
This software is in the development stage. Do not expect compatibility
|
||||
with future versions. Comments, suggestions, new features, bug fixes,
|
||||
etc. are welcome.
|
||||
|
||||
|
||||
This distribution of PyRtAudio contains the following:
|
||||
|
||||
- rtaudiomodule.cpp: the python wrapper code
|
||||
- setup.py: a setup script use to compile and install PyRtAudio
|
||||
- examples: a single PyRtAudioTest.py script
|
||||
|
||||
INSTALLATION
|
||||
|
||||
The compilation and installation of the PyRtAudio module is handled by
|
||||
the python Distribution Utilities ("Distutils"). Provided that your
|
||||
system has a C++ compiler and is properly configure, the following
|
||||
command should be sufficient:
|
||||
|
||||
>> python setup.py install
|
||||
|
||||
Please refer to the distutils documentation for installation problems: http://docs.python.org/distutils/index.html
|
||||
|
||||
LEGAL AND ETHICAL:
|
||||
|
||||
The PyRtAudio license is the same as the RtAudio license:
|
||||
|
||||
PyRtAudio: a python wrapper around RtAudio
|
||||
Copyright (c)2011 Antoine Lefebvre
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
asked to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. This is,
|
||||
however, not a binding provision of this license.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,711 @@
|
||||
/************************************************************************/
|
||||
/* PyRtAudio: a python wrapper around RtAudio
|
||||
Copyright (c) 2011 Antoine Lefebvre
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
asked to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. This is,
|
||||
however, not a binding provision of this license.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/************************************************************************/
|
||||
|
||||
// This software is in the development stage
|
||||
// Do not expect compatibility with future versions.
|
||||
// Comments, suggestions, new features, bug fixes, etc. are welcome
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "RtAudio.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PyObject_HEAD
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
void *padding; // python 2.7 seems to set dac to bad value
|
||||
// after print_function, causing a crash, no
|
||||
// idea why, but this fixes it.
|
||||
#endif
|
||||
RtAudio *dac;
|
||||
RtAudioFormat _format;
|
||||
int _bufferSize;
|
||||
unsigned int inputChannels;
|
||||
PyObject *callback_func;
|
||||
} PyRtAudio;
|
||||
|
||||
static PyObject *RtAudioErrorException;
|
||||
|
||||
static int callback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
|
||||
double streamTime, RtAudioStreamStatus status, void *data )
|
||||
{
|
||||
PyRtAudio* self = (PyRtAudio*) data;
|
||||
|
||||
if (status == RTAUDIO_OUTPUT_UNDERFLOW)
|
||||
printf("underflow.\n");
|
||||
|
||||
if (self == NULL) return -1;
|
||||
|
||||
float* in = (float *) inputBuffer;
|
||||
float* out = (float *) outputBuffer;
|
||||
|
||||
PyObject *py_callback_func = self->callback_func;
|
||||
|
||||
int retval = 0;
|
||||
|
||||
if (py_callback_func) {
|
||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject* iBuffer = PyMemoryView_FromMemory((char*)in, sizeof(float) * self->inputChannels * nBufferFrames, PyBUF_READ);
|
||||
PyObject* oBuffer = PyMemoryView_FromMemory((char*)out, sizeof(float) * nBufferFrames, PyBUF_WRITE);
|
||||
#else
|
||||
PyObject* iBuffer = PyBuffer_FromMemory(in, sizeof(float) * self->inputChannels * nBufferFrames);
|
||||
PyObject* oBuffer = PyBuffer_FromReadWriteMemory(out, sizeof(float) * nBufferFrames);
|
||||
#endif
|
||||
PyObject *arglist = Py_BuildValue("(O,O)", oBuffer, iBuffer);
|
||||
|
||||
if (arglist == NULL) {
|
||||
printf("error.\n");
|
||||
PyErr_Print();
|
||||
PyGILState_Release(gstate);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Calling the callback
|
||||
PyObject *result = PyEval_CallObject(py_callback_func, arglist);
|
||||
|
||||
if (PyErr_Occurred() != NULL) {
|
||||
PyErr_Print();
|
||||
}
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
else if (result == NULL)
|
||||
retval = 0;
|
||||
else if (PyLong_Check(result)) {
|
||||
retval = PyLong_AsLong(result);
|
||||
}
|
||||
#else
|
||||
else if (PyInt_Check(result)) {
|
||||
retval = PyInt_AsLong(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
Py_DECREF(arglist);
|
||||
Py_DECREF(oBuffer);
|
||||
Py_DECREF(iBuffer);
|
||||
Py_XDECREF(result);
|
||||
|
||||
PyGILState_Release(gstate);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void RtAudio_dealloc(PyRtAudio *self)
|
||||
{
|
||||
printf("RtAudio_dealloc.\n");
|
||||
if (self == NULL) return;
|
||||
|
||||
if (self->dac) {
|
||||
self->dac->closeStream();
|
||||
Py_CLEAR(self->callback_func);
|
||||
delete self->dac;
|
||||
}
|
||||
|
||||
Py_TYPE(self)->tp_free((PyObject *) self);
|
||||
}
|
||||
|
||||
|
||||
static PyObject* RtAudio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
printf("RtAudio_new.\n");
|
||||
PyRtAudio *self;
|
||||
char *api = NULL;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "|s", &api))
|
||||
return NULL;
|
||||
|
||||
self = (PyRtAudio *) type->tp_alloc(type, 0);
|
||||
|
||||
if(self == NULL) return NULL;
|
||||
|
||||
self->dac = NULL;
|
||||
self->callback_func = NULL;
|
||||
|
||||
try {
|
||||
if (api == NULL)
|
||||
self->dac = new RtAudio;
|
||||
else if(!strcmp(api, "jack"))
|
||||
self->dac = new RtAudio(RtAudio::UNIX_JACK);
|
||||
else if(!strcmp(api, "alsa"))
|
||||
self->dac = new RtAudio(RtAudio::LINUX_ALSA);
|
||||
else if(!strcmp(api, "oss"))
|
||||
self->dac = new RtAudio(RtAudio::LINUX_ALSA);
|
||||
else if(!strcmp(api, "core"))
|
||||
self->dac = new RtAudio(RtAudio::MACOSX_CORE);
|
||||
else if(!strcmp(api, "asio"))
|
||||
self->dac = new RtAudio(RtAudio::WINDOWS_ASIO);
|
||||
else if(!strcmp(api, "directsound"))
|
||||
self->dac = new RtAudio(RtAudio::WINDOWS_DS);
|
||||
}
|
||||
catch (RtAudioError &error) {
|
||||
PyErr_SetString(RtAudioErrorException, error.getMessage().c_str());
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->dac->showWarnings(false);
|
||||
|
||||
//Py_XINCREF(self);
|
||||
return (PyObject *) self;
|
||||
}
|
||||
|
||||
static int RtAudio_init(PyRtAudio *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
printf("RtAudio_init.\n");
|
||||
//if (self == NULL) return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This functions does not yet support all the features of the RtAudio::openStream method.
|
||||
// Please send your patches if you improves this.
|
||||
static PyObject* RtAudio_openStream(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL) return NULL;
|
||||
|
||||
if (self->dac == NULL) {
|
||||
printf("the dac is null.\n");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *oParamsObj;
|
||||
PyObject *iParamsObj;
|
||||
int fs;
|
||||
unsigned int bf;
|
||||
PyObject *pycallback;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OOiiO", &oParamsObj, &iParamsObj, &fs, &bf, &pycallback))
|
||||
return NULL;
|
||||
|
||||
RtAudio::StreamParameters oParams;
|
||||
oParams.deviceId = 1;
|
||||
oParams.nChannels = 1;
|
||||
oParams.firstChannel = 0;
|
||||
|
||||
if (PyDict_Check(oParamsObj)) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (PyDict_Contains(oParamsObj, PyUnicode_FromString("deviceId"))) {
|
||||
PyObject *value = PyDict_GetItem(oParamsObj, PyUnicode_FromString("deviceId"));
|
||||
oParams.deviceId = PyLong_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(oParamsObj, PyUnicode_FromString("nChannels"))) {
|
||||
PyObject *value = PyDict_GetItem(oParamsObj, PyUnicode_FromString("nChannels"));
|
||||
oParams.nChannels = PyLong_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(oParamsObj, PyUnicode_FromString("firstChannel"))) {
|
||||
PyObject *value = PyDict_GetItem(oParamsObj, PyUnicode_FromString("firstChannel"));
|
||||
oParams.firstChannel = PyLong_AsLong(value);
|
||||
}
|
||||
#else
|
||||
if (PyDict_Contains(oParamsObj, PyString_FromString("deviceId"))) {
|
||||
PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("deviceId"));
|
||||
oParams.deviceId = PyInt_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(oParamsObj, PyString_FromString("nChannels"))) {
|
||||
PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("nChannels"));
|
||||
oParams.nChannels = PyInt_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(oParamsObj, PyString_FromString("firstChannel"))) {
|
||||
PyObject *value = PyDict_GetItem(oParamsObj, PyString_FromString("firstChannel"));
|
||||
oParams.firstChannel = PyInt_AsLong(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
printf("First argument must be a dictionary. Default values will be used.\n");
|
||||
}
|
||||
|
||||
RtAudio::StreamParameters iParams;
|
||||
iParams.deviceId = 1;
|
||||
iParams.nChannels = 2;
|
||||
iParams.firstChannel = 0;
|
||||
|
||||
if (PyDict_Check(iParamsObj)) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (PyDict_Contains(iParamsObj, PyUnicode_FromString("deviceId"))) {
|
||||
PyObject *value = PyDict_GetItem(iParamsObj, PyUnicode_FromString("deviceId"));
|
||||
iParams.deviceId = PyLong_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(iParamsObj, PyUnicode_FromString("nChannels"))) {
|
||||
PyObject *value = PyDict_GetItem(iParamsObj, PyUnicode_FromString("nChannels"));
|
||||
iParams.nChannels = PyLong_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(iParamsObj, PyUnicode_FromString("firstChannel"))) {
|
||||
PyObject *value = PyDict_GetItem(iParamsObj, PyUnicode_FromString("firstChannel"));
|
||||
iParams.firstChannel = PyLong_AsLong(value);
|
||||
}
|
||||
#else
|
||||
if (PyDict_Contains(iParamsObj, PyString_FromString("deviceId"))) {
|
||||
PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("deviceId"));
|
||||
iParams.deviceId = PyInt_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(iParamsObj, PyString_FromString("nChannels"))) {
|
||||
PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("nChannels"));
|
||||
iParams.nChannels = PyInt_AsLong(value);
|
||||
}
|
||||
if (PyDict_Contains(iParamsObj, PyString_FromString("firstChannel"))) {
|
||||
PyObject *value = PyDict_GetItem(iParamsObj, PyString_FromString("firstChannel"));
|
||||
iParams.firstChannel = PyInt_AsLong(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
printf("Second argument must be a dictionary. Default values will be used.\n");
|
||||
}
|
||||
|
||||
|
||||
if (!PyCallable_Check(pycallback)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Need a callable object!");
|
||||
Py_XINCREF(PyExc_TypeError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// sanity check the callback ?
|
||||
|
||||
|
||||
Py_INCREF(pycallback); /* Add a reference to new callback */
|
||||
self->callback_func = pycallback; /*Remember new callback */
|
||||
|
||||
// add support for other format
|
||||
self->_format = RTAUDIO_FLOAT32;
|
||||
|
||||
// add support for other options
|
||||
RtAudio::StreamOptions options;
|
||||
options.flags = RTAUDIO_NONINTERLEAVED;
|
||||
|
||||
try {
|
||||
if (self->dac->isStreamOpen())
|
||||
self->dac->closeStream();
|
||||
self->dac->openStream(&oParams, &iParams, self->_format, fs, &bf, &callback, self, &options);
|
||||
}
|
||||
catch ( RtAudioError& error ) {
|
||||
PyErr_SetString(RtAudioErrorException, error.getMessage().c_str());
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->inputChannels = iParams.nChannels;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_closeStream(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
printf("RtAudio_closeStream.\n");
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
try {
|
||||
self->dac->closeStream();
|
||||
Py_CLEAR(self->callback_func);
|
||||
}
|
||||
catch(RtAudioError &error) {
|
||||
PyErr_SetString(RtAudioErrorException, error.getMessage().c_str());
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_startStream(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
try {
|
||||
self->dac->startStream();
|
||||
}
|
||||
catch(RtAudioError &error) {
|
||||
PyErr_SetString(RtAudioErrorException, error.getMessage().c_str());
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
static PyObject* RtAudio_stopStream(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
printf("RtAudio_stopStream.\n");
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
try {
|
||||
self->dac->stopStream();
|
||||
}
|
||||
catch(RtAudioError &error) {
|
||||
PyErr_SetString(RtAudioErrorException, error.getMessage().c_str());
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_abortStream(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
printf("RtAudio_abortStream.\n");
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
try {
|
||||
self->dac->abortStream();
|
||||
}
|
||||
catch(RtAudioError &error) {
|
||||
PyErr_SetString(RtAudioErrorException, error.getMessage().c_str());
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_isStreamRunning(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
if (self->dac == NULL) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
if (self->dac->isStreamRunning())
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_isStreamOpen(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
if (self->dac == NULL) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
if (self->dac->isStreamOpen())
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_getDeviceCount(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return PyLong_FromLong(self->dac->getDeviceCount());
|
||||
#else
|
||||
return PyInt_FromLong(self->dac->getDeviceCount());
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_getDeviceInfo(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
int device;
|
||||
if (!PyArg_ParseTuple(args, "i", &device))
|
||||
return NULL;
|
||||
|
||||
try {
|
||||
RtAudio::DeviceInfo info = self->dac->getDeviceInfo(device);
|
||||
|
||||
PyObject* info_dict = PyDict_New();
|
||||
|
||||
if (info.probed) {
|
||||
Py_INCREF(Py_True);
|
||||
PyDict_SetItemString(info_dict, "probed", Py_True);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(Py_False);
|
||||
PyDict_SetItemString(info_dict, "probed", Py_False);
|
||||
}
|
||||
PyObject* obj;
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
obj = PyUnicode_FromString(info.name.c_str());
|
||||
PyDict_SetItemString(info_dict, "name", obj);
|
||||
|
||||
obj = PyLong_FromLong(info.outputChannels);
|
||||
PyDict_SetItemString(info_dict, "outputChannels", obj);
|
||||
|
||||
obj = PyLong_FromLong(info.inputChannels);
|
||||
PyDict_SetItemString(info_dict, "inputChannels", obj);
|
||||
|
||||
obj = PyLong_FromLong(info.duplexChannels);
|
||||
PyDict_SetItemString(info_dict, "duplexChannels", obj);
|
||||
#else
|
||||
obj = PyString_FromString(info.name.c_str());
|
||||
PyDict_SetItemString(info_dict, "name", obj);
|
||||
|
||||
obj = PyInt_FromLong(info.outputChannels);
|
||||
PyDict_SetItemString(info_dict, "outputChannels", obj);
|
||||
|
||||
obj = PyInt_FromLong(info.inputChannels);
|
||||
PyDict_SetItemString(info_dict, "inputChannels", obj);
|
||||
|
||||
obj = PyInt_FromLong(info.duplexChannels);
|
||||
PyDict_SetItemString(info_dict, "duplexChannels", obj);
|
||||
#endif
|
||||
|
||||
if (info.isDefaultOutput) {
|
||||
Py_INCREF(Py_True);
|
||||
PyDict_SetItemString(info_dict, "isDefaultOutput", Py_True);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(Py_False);
|
||||
PyDict_SetItemString(info_dict, "isDefaultOutput", Py_False);
|
||||
}
|
||||
|
||||
if (info.isDefaultInput) {
|
||||
Py_INCREF(Py_True);
|
||||
PyDict_SetItemString(info_dict, "isDefaultInput", Py_True);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(Py_False);
|
||||
PyDict_SetItemString(info_dict, "isDefaultInput", Py_False);
|
||||
}
|
||||
|
||||
return info_dict;
|
||||
|
||||
}
|
||||
catch(RtAudioError &error) {
|
||||
PyErr_SetString(RtAudioErrorException, error.getMessage().c_str());
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_getDefaultOutputDevice(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return PyLong_FromLong(self->dac->getDefaultOutputDevice());
|
||||
#else
|
||||
return PyInt_FromLong(self->dac->getDefaultOutputDevice());
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_getDefaultInputDevice(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return PyLong_FromLong(self->dac->getDefaultInputDevice());
|
||||
#else
|
||||
return PyInt_FromLong(self->dac->getDefaultInputDevice());
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_getStreamTime(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
return PyFloat_FromDouble( self->dac->getStreamTime() );
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_getStreamLatency(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return PyLong_FromLong( self->dac->getStreamLatency() );
|
||||
#else
|
||||
return PyInt_FromLong( self->dac->getStreamLatency() );
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_getStreamSampleRate(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return PyLong_FromLong( self->dac->getStreamSampleRate() );
|
||||
#else
|
||||
return PyInt_FromLong( self->dac->getStreamSampleRate() );
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject* RtAudio_showWarnings(PyRtAudio *self, PyObject *args)
|
||||
{
|
||||
if (self == NULL || self->dac == NULL) return NULL;
|
||||
|
||||
PyObject *obj;
|
||||
if (!PyArg_ParseTuple(args, "O", &obj))
|
||||
return NULL;
|
||||
|
||||
if (!PyBool_Check(obj))
|
||||
return NULL;
|
||||
|
||||
if (obj == Py_True)
|
||||
self->dac->showWarnings(true);
|
||||
else if (obj == Py_False)
|
||||
self->dac->showWarnings(false);
|
||||
else {
|
||||
printf("not true nor false\n");
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef RtAudio_methods[] =
|
||||
{
|
||||
// TO BE DONE: getCurrentApi(void)
|
||||
{"getDeviceCount", (PyCFunction) RtAudio_getDeviceCount, METH_NOARGS,
|
||||
"A public function that queries for the number of audio devices available."},
|
||||
{"getDeviceInfo", (PyCFunction) RtAudio_getDeviceInfo, METH_VARARGS,
|
||||
"Return a dictionary with information for a specified device number."},
|
||||
{"getDefaultOutputDevice", (PyCFunction) RtAudio_getDefaultOutputDevice, METH_NOARGS,
|
||||
"A function that returns the index of the default output device."},
|
||||
{"getDefaultInputDevice", (PyCFunction) RtAudio_getDefaultInputDevice, METH_NOARGS,
|
||||
"A function that returns the index of the default input device."},
|
||||
{"openStream", (PyCFunction) RtAudio_openStream, METH_VARARGS,
|
||||
"A public method for opening a stream with the specified parameters."},
|
||||
{"closeStream", (PyCFunction) RtAudio_closeStream, METH_NOARGS,
|
||||
"A function that closes a stream and frees any associated stream memory. "},
|
||||
{"startStream", (PyCFunction) RtAudio_startStream, METH_NOARGS,
|
||||
"A function that starts a stream. "},
|
||||
{"stopStream", (PyCFunction) RtAudio_stopStream, METH_NOARGS,
|
||||
"Stop a stream, allowing any samples remaining in the output queue to be played. "},
|
||||
{"abortStream", (PyCFunction) RtAudio_abortStream, METH_NOARGS,
|
||||
"Stop a stream, discarding any samples remaining in the input/output queue."},
|
||||
{"isStreamOpen", (PyCFunction) RtAudio_isStreamOpen, METH_NOARGS,
|
||||
"Returns true if a stream is open and false if not."},
|
||||
{"isStreamRunning", (PyCFunction) RtAudio_isStreamRunning, METH_NOARGS,
|
||||
"Returns true if the stream is running and false if it is stopped or not open."},
|
||||
{"getStreamTime", (PyCFunction) RtAudio_getStreamTime, METH_NOARGS,
|
||||
"Returns the number of elapsed seconds since the stream was started."},
|
||||
{"getStreamLatency", (PyCFunction) RtAudio_getStreamLatency, METH_NOARGS,
|
||||
"Returns the internal stream latency in sample frames."},
|
||||
{"getStreamSampleRate", (PyCFunction) RtAudio_getStreamSampleRate, METH_NOARGS,
|
||||
"Returns actual sample rate in use by the stream."},
|
||||
{"showWarnings", (PyCFunction) RtAudio_showWarnings, METH_VARARGS,
|
||||
"Specify whether warning messages should be printed to stderr."},
|
||||
// TO BE DONE: getCompiledApi (std::vector< RtAudio::Api > &apis) throw ()
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyTypeObject RtAudio_type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"rtaudio.RtAudio", /*tp_name*/
|
||||
sizeof(RtAudio), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
(destructor) RtAudio_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*/
|
||||
"Audio input device", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
RtAudio_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
(initproc)RtAudio_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
RtAudio_new, /* tp_new */
|
||||
0, /* Low-level free-memory routine */
|
||||
0, /* For PyObject_IS_GC */
|
||||
0, // PyObject *tp_bases;
|
||||
0, // PyObject *tp_mro; /* method resolution order */
|
||||
0, //PyObject *tp_cache;
|
||||
0, //PyObject *tp_subclasses;
|
||||
0, //PyObject *tp_weaklist;
|
||||
0, //destructor tp_del;
|
||||
//0, /* Type attribute cache version tag. Added in version 2.6 */
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
static PyModuleDef RtAudio_module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"RtAudio",
|
||||
"RtAudio wrapper.",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
|
||||
#define PyMODINIT_FUNC void
|
||||
#endif
|
||||
PyMODINIT_FUNC
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyInit_rtaudio(void)
|
||||
#else
|
||||
initrtaudio(void)
|
||||
#endif
|
||||
{
|
||||
if (!PyEval_ThreadsInitialized())
|
||||
PyEval_InitThreads();
|
||||
|
||||
if (PyType_Ready(&RtAudio_type) < 0)
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return NULL;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject* module = PyModule_Create(&RtAudio_module);
|
||||
if (module == NULL)
|
||||
return NULL;
|
||||
#else
|
||||
PyObject* module = Py_InitModule3("rtaudio", NULL, "RtAudio wrapper.");
|
||||
if (module == NULL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
Py_INCREF(&RtAudio_type);
|
||||
PyModule_AddObject(module, "RtAudio", (PyObject *)&RtAudio_type);
|
||||
|
||||
RtAudioErrorException = PyErr_NewException("rtaudio.RtError", NULL, NULL);
|
||||
Py_INCREF(RtAudioErrorException);
|
||||
PyModule_AddObject(module, "RtError", RtAudioErrorException);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return module;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/bin/env python
|
||||
|
||||
import os
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
if hasattr(os, 'uname'):
|
||||
OSNAME = os.uname()[0]
|
||||
else:
|
||||
OSNAME = 'Windows'
|
||||
|
||||
|
||||
define_macros = []
|
||||
libraries = []
|
||||
extra_link_args = []
|
||||
extra_compile_args = ['-I../../../']
|
||||
sources = ['rtaudiomodule.cpp', '../../../RtAudio.cpp']
|
||||
|
||||
|
||||
if OSNAME == 'Linux':
|
||||
define_macros=[("__LINUX_ALSA__", ''),
|
||||
('__LINUX_JACK__', '')]
|
||||
libraries = ['asound', 'jack', 'pthread']
|
||||
|
||||
elif OSNAME == 'Darwin':
|
||||
define_macros = [('__MACOSX_CORE__', '')]
|
||||
libraries = ['pthread', 'stdc++']
|
||||
extra_link_args = ['-framework', 'CoreAudio']
|
||||
|
||||
elif OSNAME == 'Windows':
|
||||
define_macros = [('__WINDOWS_DS__', None),
|
||||
('__WINDOWS_ASIO__', None),
|
||||
('__LITTLE_ENDIAN__',None),
|
||||
('WIN32',None)]
|
||||
libraries = ['winmm', 'dsound', 'Advapi32','Ole32','User32']
|
||||
sources += ['../../../include/asio.cpp',
|
||||
'../../../include/asiodrivers.cpp',
|
||||
'../../../include/asiolist.cpp',
|
||||
'../../../include/iasiothiscallresolver.cpp']
|
||||
extra_compile_args.append('-I../../../include/')
|
||||
extra_compile_args.append('-EHsc')
|
||||
|
||||
|
||||
audio = Extension('rtaudio',
|
||||
sources=sources,
|
||||
libraries=libraries,
|
||||
define_macros=define_macros,
|
||||
extra_compile_args = extra_compile_args,
|
||||
extra_link_args = extra_link_args,
|
||||
)
|
||||
|
||||
|
||||
setup(name = 'rtaudio',
|
||||
version = '0.1',
|
||||
description = 'Python RtAudio interface',
|
||||
ext_modules = [audio])
|
||||
|
||||
Reference in New Issue
Block a user