250 lines
7.7 KiB
Julia
250 lines
7.7 KiB
Julia
# Low-level wrappers for Portaudio calls
|
|
|
|
# General type aliases
|
|
const PaTime = Cdouble
|
|
const PaError = Cint
|
|
const PaSampleFormat = Culong
|
|
const PaDeviceIndex = Cint
|
|
const PaHostApiIndex = Cint
|
|
const PaHostApiTypeId = Cint
|
|
# PaStream is always used as an opaque type, so we're always dealing
|
|
# with the pointer
|
|
const PaStream = Ptr{Cvoid}
|
|
const PaStreamCallback = Cvoid
|
|
const PaStreamFlags = Culong
|
|
|
|
const paNoFlag = PaStreamFlags(0x00)
|
|
|
|
const PA_NO_ERROR = 0
|
|
const PA_INPUT_OVERFLOWED = -10000 + 19
|
|
const PA_OUTPUT_UNDERFLOWED = -10000 + 20
|
|
|
|
# sample format types
|
|
const paFloat32 = PaSampleFormat(0x01)
|
|
const paInt32 = PaSampleFormat(0x02)
|
|
const paInt24 = PaSampleFormat(0x04)
|
|
const paInt16 = PaSampleFormat(0x08)
|
|
const paInt8 = PaSampleFormat(0x10)
|
|
const paUInt8 = PaSampleFormat(0x20)
|
|
const paNonInterleaved = PaSampleFormat(0x80000000)
|
|
|
|
const type_to_fmt = Dict{Type, PaSampleFormat}(
|
|
Float32 => 1,
|
|
Int32 => 2,
|
|
# Int24 => 4,
|
|
Int16 => 8,
|
|
Int8 => 16,
|
|
UInt8 => 3
|
|
)
|
|
|
|
const PaStreamCallbackResult = Cint
|
|
# Callback return values
|
|
const paContinue = PaStreamCallbackResult(0)
|
|
const paComplete = PaStreamCallbackResult(1)
|
|
const paAbort = PaStreamCallbackResult(2)
|
|
|
|
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}, ())
|
|
unsafe_string(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.
|
|
|
|
# PaHostApiTypeId values
|
|
const pa_host_api_names = Dict{PaHostApiTypeId, String}(
|
|
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"
|
|
)
|
|
|
|
mutable struct 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
|
|
|
|
mutable struct 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_GetDeviceCount() = ccall((:Pa_GetDeviceCount, libportaudio), PaDeviceIndex, ())
|
|
|
|
Pa_GetDeviceInfo(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, libportaudio),
|
|
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
|
|
|
|
Pa_GetDefaultInputDevice() = ccall((:Pa_GetDefaultInputDevice, libportaudio),
|
|
PaDeviceIndex, ())
|
|
|
|
Pa_GetDefaultOutputDevice() = ccall((:Pa_GetDefaultOutputDevice, libportaudio),
|
|
PaDeviceIndex, ())
|
|
|
|
# Stream Functions
|
|
|
|
mutable struct Pa_StreamParameters
|
|
device::PaDeviceIndex
|
|
channelCount::Cint
|
|
sampleFormat::PaSampleFormat
|
|
suggestedLatency::PaTime
|
|
hostAPISpecificStreamInfo::Ptr{Cvoid}
|
|
end
|
|
|
|
mutable struct PaStreamInfo
|
|
structVersion::Cint
|
|
inputLatency::PaTime
|
|
outputLatency::PaTime
|
|
sampleRate::Cdouble
|
|
end
|
|
|
|
# function Pa_OpenDefaultStream(inChannels, outChannels,
|
|
# sampleFormat::PaSampleFormat,
|
|
# sampleRate, framesPerBuffer)
|
|
# streamPtr = Ref{PaStream}(0)
|
|
# err = ccall((:Pa_OpenDefaultStream, libportaudio),
|
|
# PaError, (Ref{PaStream}, Cint, Cint,
|
|
# PaSampleFormat, Cdouble, Culong,
|
|
# Ref{Cvoid}, Ref{Cvoid}),
|
|
# streamPtr, inChannels, outChannels, sampleFormat, sampleRate,
|
|
# framesPerBuffer, C_NULL, C_NULL)
|
|
# handle_status(err)
|
|
#
|
|
# streamPtr[]
|
|
# end
|
|
#
|
|
function Pa_OpenStream(inParams, outParams,
|
|
sampleRate, framesPerBuffer,
|
|
flags::PaStreamFlags,
|
|
callback, userdata)
|
|
streamPtr = Ref{PaStream}(0)
|
|
err = ccall((:Pa_OpenStream, libportaudio), PaError,
|
|
(Ref{PaStream},
|
|
Ptr{Pa_StreamParameters},
|
|
Ptr{Pa_StreamParameters},
|
|
Cdouble, Culong, PaStreamFlags,
|
|
Ptr{Cvoid}, Ptr{Cvoid}),
|
|
streamPtr,
|
|
inParams, outParams,
|
|
sampleRate, framesPerBuffer, flags,
|
|
callback, userdata)
|
|
handle_status(err)
|
|
streamPtr[]
|
|
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{Cvoid}, 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{Cvoid}, Culong),
|
|
stream, buf, frames)
|
|
handle_status(err, show_warnings)
|
|
nothing
|
|
end
|
|
|
|
# function Pa_GetStreamInfo(stream::PaStream)
|
|
# infoptr = ccall((:Pa_GetStreamInfo, libportaudio), Ptr{PaStreamInfo},
|
|
# (PaStream, ), stream)
|
|
#
|
|
# unsafe_load(infoptr)
|
|
# 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: " * unsafe_string(msg))
|
|
end
|
|
elseif err != PA_NO_ERROR
|
|
msg = ccall((:Pa_GetErrorText, libportaudio),
|
|
Ptr{Cchar}, (PaError,), err)
|
|
error("libportaudio: " * unsafe_string(msg))
|
|
end
|
|
end
|