now using a pure-julia portaudio wrapper and the read/write API
This commit is contained in:
parent
f4adf31d15
commit
eaca8109f5
7 changed files with 154 additions and 244 deletions
3
deps/build.jl
vendored
3
deps/build.jl
vendored
|
@ -25,6 +25,3 @@ end
|
||||||
|
|
||||||
@BinDeps.install [:libportaudio => :libportaudio,
|
@BinDeps.install [:libportaudio => :libportaudio,
|
||||||
:libsndfile => :libsndfile]
|
:libsndfile => :libsndfile]
|
||||||
|
|
||||||
# cd(Pkg.dir("AudioIO", "deps", "src"))
|
|
||||||
# run(`make`)
|
|
||||||
|
|
63
deps/src/Makefile
vendored
63
deps/src/Makefile
vendored
|
@ -1,63 +0,0 @@
|
||||||
# Makefile lifted from Clang.jl
|
|
||||||
|
|
||||||
all: default
|
|
||||||
|
|
||||||
ifeq (exists, $(shell [ -e Make.user ] && echo exists ))
|
|
||||||
include Make.user
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: all clean check-env default
|
|
||||||
|
|
||||||
#check-env:
|
|
||||||
#ifndef JULIA_INC
|
|
||||||
# $(error Environment variable JULIA_INC is not set.)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#INC =-I"$(JULIA_INC)"
|
|
||||||
FLAGS =-Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fPIC
|
|
||||||
CFLAGS =-g
|
|
||||||
LIBS =-L/usr/local/lib -lportaudio
|
|
||||||
LINUX_LIBS =-lrt
|
|
||||||
LINUX_LDFLAGS =-rdynamic
|
|
||||||
# add the Homebrew.jl tree to the include dirs in case we used it for
|
|
||||||
# portaudio and libsndfile
|
|
||||||
DARWIN_LDFLAGS =-L../../../Homebrew/deps/usr/lib
|
|
||||||
DARWIN_INC =-I../../../Homebrew/deps/usr/include
|
|
||||||
TARGETDIR=../usr/lib
|
|
||||||
|
|
||||||
OBJS = shim.o
|
|
||||||
|
|
||||||
# Figure out OS and architecture
|
|
||||||
OS = $(shell uname)
|
|
||||||
ifeq ($(OS), MINGW32_NT-6.1)
|
|
||||||
OS=WINNT
|
|
||||||
endif
|
|
||||||
|
|
||||||
# file extensions and platform-specific libs
|
|
||||||
ifeq ($(OS), WINNT)
|
|
||||||
SHLIB_EXT = dll
|
|
||||||
else ifeq ($(OS), Darwin)
|
|
||||||
SHLIB_EXT = dylib
|
|
||||||
INC += $(DARWIN_INC)
|
|
||||||
LDFLAGS += $(DARWIN_LDFLAGS)
|
|
||||||
else
|
|
||||||
LIBS += $(LINUX_LIBS)
|
|
||||||
LDFLAGS += $(LINUX_LDFLAGS)
|
|
||||||
SHLIB_EXT = so
|
|
||||||
INC += $(LINUX_INC)
|
|
||||||
endif
|
|
||||||
|
|
||||||
TARGET=$(TARGETDIR)/libportaudio_shim.$(SHLIB_EXT)
|
|
||||||
|
|
||||||
default: $(TARGET)
|
|
||||||
|
|
||||||
%.o: %.c Makefile
|
|
||||||
$(CC) $< -fPIC -c -o $@ $(INC) $(CFLAGS) $(FLAGS)
|
|
||||||
|
|
||||||
$(TARGET): $(OBJS) Makefile
|
|
||||||
mkdir -p $(TARGETDIR)
|
|
||||||
$(CC) $(OBJS) -shared -o $@ $(LDFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o
|
|
||||||
rm -f $(TARGET)
|
|
93
deps/src/shim.c
vendored
93
deps/src/shim.c
vendored
|
@ -1,93 +0,0 @@
|
||||||
#include <portaudio.h>
|
|
||||||
#include <semaphore.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define SHIM_REVISION 2
|
|
||||||
|
|
||||||
int paCallback(const void *inputBuffer, void *outputBuffer,
|
|
||||||
unsigned long framesPerBuffer,
|
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
|
||||||
PaStreamCallbackFlags statusFlags,
|
|
||||||
void *userData);
|
|
||||||
|
|
||||||
static PaStream *AudioStream;
|
|
||||||
static int JuliaPipeReadFD = 0;
|
|
||||||
static int JuliaPipeWriteFD = 0;
|
|
||||||
static sem_t CSemaphore;
|
|
||||||
static void *Buffer = NULL;
|
|
||||||
|
|
||||||
int make_pipe(void)
|
|
||||||
{
|
|
||||||
int pipefd[2];
|
|
||||||
pipe(pipefd);
|
|
||||||
JuliaPipeReadFD = pipefd[0];
|
|
||||||
JuliaPipeWriteFD = pipefd[1];
|
|
||||||
sem_init(&CSemaphore, 0, 0);
|
|
||||||
return JuliaPipeReadFD;
|
|
||||||
}
|
|
||||||
|
|
||||||
void synchronize_buffer(void *buffer)
|
|
||||||
{
|
|
||||||
Buffer = buffer;
|
|
||||||
sem_post(&CSemaphore);
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_shim_revision(void)
|
|
||||||
{
|
|
||||||
return SHIM_REVISION;
|
|
||||||
}
|
|
||||||
|
|
||||||
PaError open_stream(unsigned int sampleRate, unsigned int bufSize)
|
|
||||||
{
|
|
||||||
PaError err;
|
|
||||||
|
|
||||||
err = Pa_OpenDefaultStream(&AudioStream,
|
|
||||||
1, /* single input channel */
|
|
||||||
1, /* mono output */
|
|
||||||
paFloat32, /* 32 bit floating point output */
|
|
||||||
sampleRate,
|
|
||||||
bufSize, /* frames per buffer, i.e. the number of sample frames
|
|
||||||
that PortAudio will request from the callback. Many
|
|
||||||
apps may want to use paFramesPerBufferUnspecified,
|
|
||||||
which tells PortAudio to pick the best, possibly
|
|
||||||
changing, buffer size.*/
|
|
||||||
paCallback, /* this is your callback function */
|
|
||||||
NULL); /*This is a pointer that will be passed to your callback*/
|
|
||||||
if(err != paNoError)
|
|
||||||
{
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = Pa_StartStream(AudioStream);
|
|
||||||
if(err != paNoError)
|
|
||||||
{
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return paNoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This routine will be called by the PortAudio engine when audio is needed.
|
|
||||||
* It may called at interrupt level on some machines so don't do anything that
|
|
||||||
* could mess up the system like calling malloc() or free().
|
|
||||||
*/
|
|
||||||
int paCallback(const void *inputBuffer, void *outputBuffer,
|
|
||||||
unsigned long framesPerBuffer,
|
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
|
||||||
PaStreamCallbackFlags statusFlags,
|
|
||||||
void *userData)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
unsigned char fd_data = 0;
|
|
||||||
|
|
||||||
sem_wait(&CSemaphore);
|
|
||||||
for(i=0; i<framesPerBuffer; i++)
|
|
||||||
{
|
|
||||||
((float *)outputBuffer)[i] = ((float *)Buffer)[i];
|
|
||||||
((float *)Buffer)[i] = ((float *)inputBuffer)[i];
|
|
||||||
}
|
|
||||||
write(JuliaPipeWriteFD, &fd_data, 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
BIN
deps/usr/lib/libportaudio_shim.dylib
vendored
BIN
deps/usr/lib/libportaudio_shim.dylib
vendored
Binary file not shown.
BIN
deps/usr/lib/libportaudio_shim.so
vendored
BIN
deps/usr/lib/libportaudio_shim.so
vendored
Binary file not shown.
|
@ -17,8 +17,10 @@ typealias AudioBuf Array{AudioSample}
|
||||||
abstract AudioRenderer
|
abstract AudioRenderer
|
||||||
|
|
||||||
# A stream of audio (for instance that writes to hardware). All AudioStream
|
# A stream of audio (for instance that writes to hardware). All AudioStream
|
||||||
# subtypes should have a mixer and info field
|
# subtypes should have a root and info field
|
||||||
abstract AudioStream
|
abstract AudioStream
|
||||||
|
samplerate(str::AudioStream) = str.info.sample_rate
|
||||||
|
bufsize(str::AudioStream) = str.info.buf_size
|
||||||
|
|
||||||
# An audio interface is usually a physical sound card, but could
|
# An audio interface is usually a physical sound card, but could
|
||||||
# be anything you'd want to connect a stream to
|
# be anything you'd want to connect a stream to
|
||||||
|
|
235
src/portaudio.jl
235
src/portaudio.jl
|
@ -1,17 +1,25 @@
|
||||||
typealias PaTime Cdouble
|
typealias PaTime Cdouble
|
||||||
typealias PaError Cint
|
typealias PaError Cint
|
||||||
typealias PaSampleFormat Culong
|
typealias PaSampleFormat Culong
|
||||||
typealias PaStream Void
|
# PaStream is always used as an opaque type, so we're always dealing with the
|
||||||
|
# pointer
|
||||||
|
typealias PaStream Ptr{Void}
|
||||||
typealias PaDeviceIndex Cint
|
typealias PaDeviceIndex Cint
|
||||||
typealias PaHostApiIndex Cint
|
typealias PaHostApiIndex Cint
|
||||||
typealias PaTime Cdouble
|
typealias PaTime Cdouble
|
||||||
typealias PaHostApiTypeId Cint
|
typealias PaHostApiTypeId Cint
|
||||||
|
typealias PaStreamCallback Void
|
||||||
|
|
||||||
const PA_NO_ERROR = 0
|
const PA_NO_ERROR = 0
|
||||||
# expected shim revision, so we can notify if it gets out of sync
|
const PA_INPUT_OVERFLOWED = -10000 + 19
|
||||||
const SHIM_REVISION = 2
|
const PA_OUTPUT_UNDERFLOWED = -10000 + 20
|
||||||
const libportaudio_shim = find_library(["libportaudio_shim",],
|
|
||||||
[Pkg.dir("AudioIO", "deps", "usr", "lib"),])
|
const paFloat32 = convert(PaSampleFormat, 0x01)
|
||||||
|
const paInt32 = convert(PaSampleFormat, 0x02)
|
||||||
|
const paInt24 = convert(PaSampleFormat, 0x04)
|
||||||
|
const paInt16 = convert(PaSampleFormat, 0x08)
|
||||||
|
const paInt8 = convert(PaSampleFormat, 0x10)
|
||||||
|
const paUInt8 = convert(PaSampleFormat, 0x20)
|
||||||
|
|
||||||
# PaHostApiTypeId values
|
# PaHostApiTypeId values
|
||||||
const pa_host_api_names = {
|
const pa_host_api_names = {
|
||||||
|
@ -39,91 +47,64 @@ portaudio_inited = false
|
||||||
type PortAudioStream <: AudioStream
|
type PortAudioStream <: AudioStream
|
||||||
root::AudioMixer
|
root::AudioMixer
|
||||||
info::DeviceInfo
|
info::DeviceInfo
|
||||||
|
stream::PaStream
|
||||||
|
|
||||||
function PortAudioStream(sample_rate::Int=44100, buf_size::Int=1024)
|
function PortAudioStream(sample_rate::Integer=44100, buf_size::Integer=1024)
|
||||||
init_portaudio()
|
require_portaudio_init()
|
||||||
|
stream = Pa_OpenDefaultStream(1, 1, paFloat32, sample_rate, buf_size)
|
||||||
|
Pa_StartStream(stream)
|
||||||
root = AudioMixer()
|
root = AudioMixer()
|
||||||
stream = new(root, DeviceInfo(sample_rate, buf_size))
|
this = new(root, DeviceInfo(sample_rate, buf_size), stream)
|
||||||
# we need to start up the stream with the portaudio library
|
info("Scheduling PortAudio Render Task...")
|
||||||
open_portaudio_stream(stream)
|
# the task will actually start running the next time the current task yields
|
||||||
return stream
|
@schedule(portaudio_task(this))
|
||||||
|
finalizer(this, destroy)
|
||||||
|
|
||||||
|
this
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function destroy(stream::PortAudioStream)
|
||||||
|
# in 0.3 we can't print from a finalizer, as STDOUT may have been GC'ed
|
||||||
|
# already and we get a segfault. See
|
||||||
|
# https://github.com/JuliaLang/julia/issues/6075
|
||||||
|
#info("Cleaning up stream")
|
||||||
|
Pa_StopStream(stream.stream)
|
||||||
|
Pa_CloseStream(stream.stream)
|
||||||
|
# we only have 1 stream at a time, so if we're closing out we can just
|
||||||
|
# terminate PortAudio.
|
||||||
|
Pa_Terminate()
|
||||||
|
portaudio_inited = false
|
||||||
|
end
|
||||||
|
|
||||||
############ Internal Functions ############
|
############ Internal Functions ############
|
||||||
|
|
||||||
function synchronize_buffer(buffer)
|
function portaudio_task(stream::PortAudioStream)
|
||||||
ccall((:synchronize_buffer, libportaudio_shim), Void, (Ptr{Void},), buffer)
|
info("PortAudio Render Task Running...")
|
||||||
end
|
n = bufsize(stream)
|
||||||
|
buffer = zeros(AudioSample, n)
|
||||||
function open_portaudio_stream(stream::PortAudioStream)
|
|
||||||
# starts up a stream with the portaudio library and associates it with the
|
|
||||||
# given AudioIO PortAudioStream
|
|
||||||
|
|
||||||
# TODO: handle more streams
|
|
||||||
|
|
||||||
shim_rev = ccall((:get_shim_revision, libportaudio_shim), Cint, ())
|
|
||||||
if shim_rev != SHIM_REVISION
|
|
||||||
error("Expected shim revision $SHIM_REVISION, got $shim_rev. Run 'make' from AudioIO/deps/src")
|
|
||||||
end
|
|
||||||
fd = ccall((:make_pipe, libportaudio_shim), Cint, ())
|
|
||||||
|
|
||||||
info("Launching PortAudio Task...")
|
|
||||||
schedule(Task(() -> portaudio_task(fd, stream)))
|
|
||||||
# TODO: test not yielding here
|
|
||||||
yield()
|
|
||||||
info("Audio Task Yielded, starting the stream...")
|
|
||||||
|
|
||||||
err = ccall((:open_stream, libportaudio_shim), PaError,
|
|
||||||
(Cuint, Cuint),
|
|
||||||
stream.info.sample_rate, stream.info.buf_size)
|
|
||||||
handle_status(err)
|
|
||||||
info("Portaudio stream started.")
|
|
||||||
end
|
|
||||||
|
|
||||||
function handle_status(err::PaError)
|
|
||||||
if err != PA_NO_ERROR
|
|
||||||
msg = ccall((:Pa_GetErrorText, libportaudio),
|
|
||||||
Ptr{Cchar}, (PaError,), err)
|
|
||||||
error("libportaudio: " * bytestring(msg))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function portaudio_task(jl_filedesc::Integer, stream::PortAudioStream)
|
|
||||||
buffer = zeros(AudioSample, stream.info.buf_size)
|
|
||||||
desc_bytes = Cchar[0]
|
|
||||||
jl_stream = fdio(jl_filedesc)
|
|
||||||
jl_rawfd = RawFD(jl_filedesc)
|
|
||||||
try
|
try
|
||||||
while true
|
while true
|
||||||
|
while Pa_GetStreamReadAvailable(stream.stream) < n
|
||||||
|
sleep(0.005)
|
||||||
|
end
|
||||||
|
Pa_ReadStream(stream.stream, buffer, n)
|
||||||
# assume the root is always active
|
# assume the root is always active
|
||||||
rendered = render(stream.root.renderer, buffer, stream.info)::AudioBuf
|
rendered = render(stream.root.renderer, buffer, stream.info)::AudioBuf
|
||||||
for i in 1:length(rendered)
|
for i in 1:length(rendered)
|
||||||
buffer[i] = rendered[i]
|
buffer[i] = rendered[i]
|
||||||
end
|
end
|
||||||
for i in (length(rendered)+1):length(buffer)
|
for i in (length(rendered)+1):n
|
||||||
buffer[i] = 0.0
|
buffer[i] = 0.0
|
||||||
end
|
end
|
||||||
|
while Pa_GetStreamWriteAvailable(stream.stream) < n
|
||||||
# wake the C code so it knows we've given it some more data
|
sleep(0.005)
|
||||||
synchronize_buffer(buffer)
|
end
|
||||||
# wait for new data to be available from the sound card (and for it
|
Pa_WriteStream(stream.stream, buffer, n)
|
||||||
# to have processed our last frame of data). At some point we
|
|
||||||
# should do something with the data we get from the callback
|
|
||||||
wait(jl_rawfd, readable=true)
|
|
||||||
# read from the file descriptor so that it's empty. We're using
|
|
||||||
# ccall here because readbytes() was blocking the whole julia
|
|
||||||
# thread. This shouldn't block at all because we just waited on it
|
|
||||||
ccall(:read, Clong, (Cint, Ptr{Void}, Culong),
|
|
||||||
jl_filedesc, desc_bytes, 1)
|
|
||||||
end
|
end
|
||||||
catch ex
|
catch ex
|
||||||
warn("Audio Task died with exception: $ex")
|
warn("Audio Task died with exception: $ex")
|
||||||
Base.show_backtrace(STDOUT, catch_backtrace())
|
Base.show_backtrace(STDOUT, catch_backtrace())
|
||||||
finally
|
|
||||||
# TODO: we need to close the stream here. Otherwise the audio callback
|
|
||||||
# will segfault accessing the output array if there were exceptions
|
|
||||||
# thrown in the render loop
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -156,32 +137,118 @@ type PortAudioInterface <: AudioInterface
|
||||||
max_output_channels::Int
|
max_output_channels::Int
|
||||||
end
|
end
|
||||||
|
|
||||||
# some thin wrappers to portaudio calls
|
|
||||||
get_device_info(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, libportaudio),
|
|
||||||
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
|
|
||||||
get_host_api_info(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, libportaudio),
|
|
||||||
Ptr{PaHostApiInfo}, (PaHostApiIndex,), i))
|
|
||||||
|
|
||||||
function get_portaudio_devices()
|
function get_portaudio_devices()
|
||||||
init_portaudio()
|
require_portaudio_init()
|
||||||
device_count = ccall((:Pa_GetDeviceCount, libportaudio), PaDeviceIndex, ())
|
device_count = ccall((:Pa_GetDeviceCount, libportaudio), PaDeviceIndex, ())
|
||||||
pa_devices = [get_device_info(i) for i in 0:(device_count - 1)]
|
pa_devices = [Pa_GetDeviceInfo(i) for i in 0:(device_count - 1)]
|
||||||
[PortAudioInterface(bytestring(d.name),
|
[PortAudioInterface(bytestring(d.name),
|
||||||
bytestring(get_host_api_info(d.host_api).name),
|
bytestring(Pa_GetHostApiInfo(d.host_api).name),
|
||||||
d.max_input_channels,
|
d.max_input_channels,
|
||||||
d.max_output_channels)
|
d.max_output_channels)
|
||||||
for d in pa_devices]
|
for d in pa_devices]
|
||||||
end
|
end
|
||||||
|
|
||||||
function init_portaudio()
|
function require_portaudio_init()
|
||||||
# can be called multiple times with no effect
|
# can be called multiple times with no effect
|
||||||
global portaudio_inited
|
global portaudio_inited
|
||||||
if !portaudio_inited
|
if !portaudio_inited
|
||||||
@assert(libportaudio_shim != "", "Failed to find required library libportaudio_shim. Try re-running the package script using Pkg.build(\"AudioIO\"), then reloading with reload(\"AudioIO\")")
|
|
||||||
|
|
||||||
info("Initializing PortAudio. Expect errors as we scan devices")
|
info("Initializing PortAudio. Expect errors as we scan devices")
|
||||||
err = ccall((:Pa_Initialize, libportaudio), PaError, ())
|
Pa_Initialize()
|
||||||
handle_status(err)
|
|
||||||
portaudio_inited = true
|
portaudio_inited = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Low-level wrappers for Portaudio calls
|
||||||
|
Pa_GetDeviceInfo(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, libportaudio),
|
||||||
|
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
|
||||||
|
Pa_GetHostApiInfo(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, libportaudio),
|
||||||
|
Ptr{PaHostApiInfo}, (PaHostApiIndex,), i))
|
||||||
|
|
||||||
|
function Pa_Initialize()
|
||||||
|
err = ccall((:Pa_Initialize, libportaudio), PaError, ())
|
||||||
|
handle_status(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_Terminate()
|
||||||
|
err = ccall((:Pa_Terminate, libportaudio), PaError, ())
|
||||||
|
handle_status(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_StartStream(stream::PaStream)
|
||||||
|
err = ccall((:Pa_StartStream, libportaudio), PaError,
|
||||||
|
(PaStream,), stream)
|
||||||
|
handle_status(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_StopStream(stream::PaStream)
|
||||||
|
err = ccall((:Pa_StopStream, libportaudio), PaError,
|
||||||
|
(PaStream,), stream)
|
||||||
|
handle_status(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_CloseStream(stream::PaStream)
|
||||||
|
err = ccall((:Pa_CloseStream, libportaudio), PaError,
|
||||||
|
(PaStream,), stream)
|
||||||
|
handle_status(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_GetStreamReadAvailable(stream::PaStream)
|
||||||
|
avail = ccall((:Pa_GetStreamReadAvailable, libportaudio), Clong,
|
||||||
|
(PaStream,), stream)
|
||||||
|
avail >= 0 || handle_status(avail)
|
||||||
|
avail
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_GetStreamWriteAvailable(stream::PaStream)
|
||||||
|
avail = ccall((:Pa_GetStreamWriteAvailable, libportaudio), Clong,
|
||||||
|
(PaStream,), stream)
|
||||||
|
avail >= 0 || handle_status(avail)
|
||||||
|
avail
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_ReadStream(stream::PaStream, buf::Array, frames::Integer=length(buf))
|
||||||
|
frames <= length(buf) || error("Need a buffer at least $frames long")
|
||||||
|
err = ccall((:Pa_ReadStream, libportaudio), PaError,
|
||||||
|
(PaStream, Ptr{Void}, Culong),
|
||||||
|
stream, buf, frames)
|
||||||
|
handle_status(err)
|
||||||
|
buf
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_WriteStream(stream::PaStream, buf::Array, frames::Integer=length(buf))
|
||||||
|
frames <= length(buf) || error("Need a buffer at least $frames long")
|
||||||
|
err = ccall((:Pa_WriteStream, libportaudio), PaError,
|
||||||
|
(PaStream, Ptr{Void}, Culong),
|
||||||
|
stream, buf, frames)
|
||||||
|
handle_status(err)
|
||||||
|
nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
Pa_GetVersion() = ccall((:Pa_GetVersion, libportaudio), Cint, ())
|
||||||
|
|
||||||
|
function Pa_OpenDefaultStream(inChannels::Integer, outChannels::Integer,
|
||||||
|
sampleFormat::PaSampleFormat,
|
||||||
|
sampleRate::Real, framesPerBuffer::Integer)
|
||||||
|
streamPtr::Array{PaStream} = PaStream[0]
|
||||||
|
err = ccall((:Pa_OpenDefaultStream, libportaudio),
|
||||||
|
PaError, (Ptr{PaStream}, Cint, Cint,
|
||||||
|
PaSampleFormat, Cdouble, Culong,
|
||||||
|
Ptr{PaStreamCallback}, Ptr{Void}),
|
||||||
|
streamPtr, inChannels, outChannels, sampleFormat, sampleRate,
|
||||||
|
framesPerBuffer, 0, 0)
|
||||||
|
handle_status(err)
|
||||||
|
|
||||||
|
streamPtr[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
function handle_status(err::PaError)
|
||||||
|
if err == PA_OUTPUT_UNDERFLOWED || err == PA_INPUT_OVERFLOWED
|
||||||
|
msg = ccall((:Pa_GetErrorText, libportaudio),
|
||||||
|
Ptr{Cchar}, (PaError,), err)
|
||||||
|
warn("libportaudio: " * bytestring(msg))
|
||||||
|
elseif err != PA_NO_ERROR
|
||||||
|
msg = ccall((:Pa_GetErrorText, libportaudio),
|
||||||
|
Ptr{Cchar}, (PaError,), err)
|
||||||
|
error("libportaudio: " * bytestring(msg))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue