231 lines
7.5 KiB
Julia
231 lines
7.5 KiB
Julia
|
# Low-level wrappers for Portaudio calls
|
||
|
|
||
|
# General type aliases
|
||
|
typealias PaTime Cdouble
|
||
|
typealias PaError Cint
|
||
|
typealias PaSampleFormat Culong
|
||
|
|
||
|
const PA_NO_ERROR = 0
|
||
|
const PA_INPUT_OVERFLOWED = -10000 + 19
|
||
|
const PA_OUTPUT_UNDERFLOWED = -10000 + 20
|
||
|
|
||
|
const paFloat32 = PaSampleFormat(0x01)
|
||
|
const paInt32 = PaSampleFormat(0x02)
|
||
|
const paInt24 = PaSampleFormat(0x04)
|
||
|
const paInt16 = PaSampleFormat(0x08)
|
||
|
const paInt8 = PaSampleFormat(0x10)
|
||
|
const paUInt8 = PaSampleFormat(0x20)
|
||
|
|
||
|
@compat const pa_sample_formats = Dict{PaSampleFormat, Type}(
|
||
|
1 => Float32
|
||
|
2 => Int32
|
||
|
4 => Int24
|
||
|
8 => Int16
|
||
|
16 => Int8
|
||
|
32 => UInt8
|
||
|
)
|
||
|
|
||
|
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
|
||
|
|
||
|
Pa_GetVersion() = ccall((:Pa_GetVersion, libportaudio), Cint, ())
|
||
|
|
||
|
function Pa_GetVersionText()
|
||
|
versionPtr = ccall((:Pa_GetVersionText, libportaudio), Ptr{Cchar}, ())
|
||
|
bytestring(versionPtr)
|
||
|
end
|
||
|
|
||
|
# Host API Functions
|
||
|
|
||
|
# A Host API is the top-level of the PortAudio hierarchy. Each host API has a
|
||
|
# unique type ID that tells you which native backend it is (JACK, ALSA, ASIO,
|
||
|
# etc.). On a given system you can identify each backend by its index, which
|
||
|
# will range between 0 and Pa_GetHostApiCount() - 1. You can enumerate through
|
||
|
# all the host APIs on the system by iterating through those values.
|
||
|
|
||
|
typealias PaHostApiIndex Cint
|
||
|
typealias PaHostApiTypeId Cint
|
||
|
|
||
|
# PaHostApiTypeId values
|
||
|
@compat const pa_host_api_names = Dict{PaHostApiTypeId, ASCIIString}(
|
||
|
0 => "In Development", # use while developing support for a new host API
|
||
|
1 => "Direct Sound",
|
||
|
2 => "MME",
|
||
|
3 => "ASIO",
|
||
|
4 => "Sound Manager",
|
||
|
5 => "Core Audio",
|
||
|
7 => "OSS",
|
||
|
8 => "ALSA",
|
||
|
9 => "AL",
|
||
|
10 => "BeOS",
|
||
|
11 => "WDMKS",
|
||
|
12 => "Jack",
|
||
|
13 => "WASAPI",
|
||
|
14 => "AudioScience HPI"
|
||
|
)
|
||
|
|
||
|
type PaHostApiInfo
|
||
|
struct_version::Cint
|
||
|
api_type::PaHostApiTypeId
|
||
|
name::Ptr{Cchar}
|
||
|
deviceCount::Cint
|
||
|
defaultInputDevice::PaDeviceIndex
|
||
|
defaultOutputDevice::PaDeviceIndex
|
||
|
end
|
||
|
|
||
|
Pa_GetHostApiInfo(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, libportaudio),
|
||
|
Ptr{PaHostApiInfo}, (PaHostApiIndex,), i))
|
||
|
|
||
|
# Device Functions
|
||
|
|
||
|
typealias PaDeviceIndex Cint
|
||
|
|
||
|
type PaDeviceInfo
|
||
|
struct_version::Cint
|
||
|
name::Ptr{Cchar}
|
||
|
host_api::PaHostApiIndex
|
||
|
max_input_channels::Cint
|
||
|
max_output_channels::Cint
|
||
|
default_low_input_latency::PaTime
|
||
|
default_low_output_latency::PaTime
|
||
|
default_high_input_latency::PaTime
|
||
|
default_high_output_latency::PaTime
|
||
|
default_sample_rate::Cdouble
|
||
|
end
|
||
|
|
||
|
Pa_GetDeviceInfo(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, libportaudio),
|
||
|
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
|
||
|
|
||
|
# Stream Functions
|
||
|
|
||
|
# PaStream is always used as an opaque type, so we're always dealing
|
||
|
# with the pointer
|
||
|
typealias PaStream Ptr{Void}
|
||
|
typealias PaStreamCallback Void
|
||
|
typealias PaStreamFlags Culong
|
||
|
|
||
|
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
|
||
|
|
||
|
"""
|
||
|
Open a single stream, not necessarily the default one
|
||
|
The stream is unidirectional, either inout or default output
|
||
|
see http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html
|
||
|
"""
|
||
|
function Pa_OpenStream(device::PaDeviceIndex,
|
||
|
channels::Cint, input::Bool,
|
||
|
sampleFormat::PaSampleFormat,
|
||
|
sampleRate::Cdouble, framesPerBuffer::Culong)
|
||
|
streamPtr::Array{PaStream} = PaStream[0]
|
||
|
ioParameters = Pa_StreamParameters(device, channels,
|
||
|
sampleFormat, PaTime(0.001),
|
||
|
Ptr{Void}(0))
|
||
|
# CURRENTLY WORKING THIS OUT
|
||
|
if input
|
||
|
err = ccall((:Pa_OpenStream, libportaudio), PaError,
|
||
|
(PaStream,
|
||
|
Ptr{Pa_StreamParameters}, Ptr{Pa_StreamParameters},
|
||
|
Cdouble, Culong, PaStreamFlags,
|
||
|
Ptr{PaStreamCallback}, Ptr{Void}),
|
||
|
streamPtr, ioParameters, Ptr{Void}(0),
|
||
|
sampleRate, framesPerBuffer, 0,
|
||
|
Ptr{PaStreamCallback}(0), Ptr{Void}(0))
|
||
|
else
|
||
|
err = ccall((:Pa_OpenStream, libportaudio), PaError,
|
||
|
(PaStream, Ptr{Void}, Ref{Pa_StreamParameters},
|
||
|
Cdouble, Culong, Culong,
|
||
|
Ptr{PaStreamCallback}, Ptr{Void}),
|
||
|
streamPtr, Ptr{Void}(0), ioParameters,
|
||
|
sampleRate, framesPerBuffer, 0,
|
||
|
Ptr{PaStreamCallback}(0), Ptr{Void}(0))
|
||
|
end
|
||
|
handle_status(err)
|
||
|
streamPtr[1]
|
||
|
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),
|
||
|
show_warnings::Bool=true)
|
||
|
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, show_warnings)
|
||
|
buf
|
||
|
end
|
||
|
|
||
|
function Pa_WriteStream(stream::PaStream, buf::Array, frames::Integer=length(buf),
|
||
|
show_warnings::Bool=true)
|
||
|
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, show_warnings)
|
||
|
nothing
|
||
|
end
|
||
|
|
||
|
# General utility function to handle the status from the Pa_* functions
|
||
|
function handle_status(err::PaError, show_warnings::Bool=true)
|
||
|
if err == PA_OUTPUT_UNDERFLOWED || err == PA_INPUT_OVERFLOWED
|
||
|
if show_warnings
|
||
|
msg = ccall((:Pa_GetErrorText, libportaudio),
|
||
|
Ptr{Cchar}, (PaError,), err)
|
||
|
warn("libportaudio: " * bytestring(msg))
|
||
|
end
|
||
|
elseif err != PA_NO_ERROR
|
||
|
msg = ccall((:Pa_GetErrorText, libportaudio),
|
||
|
Ptr{Cchar}, (PaError,), err)
|
||
|
error("libportaudio: " * bytestring(msg))
|
||
|
end
|
||
|
end
|