Update portaudio.jl

This commit is contained in:
Bill 2015-11-03 18:58:57 -10:00
parent 348e2576f9
commit 826fdafe5f

View file

@ -23,7 +23,7 @@ const paInt8 = convert(PaSampleFormat, 0x10)
const paUInt8 = convert(PaSampleFormat, 0x20) const paUInt8 = convert(PaSampleFormat, 0x20)
# PaHostApiTypeId values # PaHostApiTypeId values
const pa_host_api_names = { @compat const pa_host_api_names = (
0 => "In Development", # use while developing support for a new host API 0 => "In Development", # use while developing support for a new host API
1 => "Direct Sound", 1 => "Direct Sound",
2 => "MME", 2 => "MME",
@ -38,7 +38,7 @@ const pa_host_api_names = {
12 => "Jack", 12 => "Jack",
13 => "WASAPI", 13 => "WASAPI",
14 => "AudioScience HPI" 14 => "AudioScience HPI"
} )
# track whether we've already inited PortAudio # track whether we've already inited PortAudio
portaudio_inited = false portaudio_inited = false
@ -90,15 +90,15 @@ type Pa_StreamParameters
hostAPISpecificStreamInfo::Ptr{Void} hostAPISpecificStreamInfo::Ptr{Void}
end 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, function Pa_OpenStream(device::PaDeviceIndex,
channels::Cint, input::Bool, channels::Cint, input::Bool,
sampleFormat::PaSampleFormat, sampleFormat::PaSampleFormat,
sampleRate::Cdouble, framesPerBuffer::Culong) sampleRate::Cdouble, framesPerBuffer::Culong)
#=
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
=#
streamPtr::Array{PaStream} = PaStream[0] streamPtr::Array{PaStream} = PaStream[0]
ioParameters = Pa_StreamParameters(device, channels, ioParameters = Pa_StreamParameters(device, channels,
sampleFormat, PaTime(0.001), sampleFormat, PaTime(0.001),
@ -131,17 +131,18 @@ type Pa_AudioStream <: AudioStream
stream::PaStream stream::PaStream
sformat::PaSampleFormat sformat::PaSampleFormat
sbuffer::Array{Real} sbuffer::Array{Real}
sbuffer_output_waiting::Integer
parent_may_use_buffer::Bool parent_may_use_buffer::Bool
"""
Get device parameters needed for opening with portaudio
default is input as 44100/16bit int, same as CD audio type input
"""
function Pa_AudioStream(device_index, channels=2, input=false, function Pa_AudioStream(device_index, channels=2, input=false,
sample_rate::Integer=44100, sample_rate::Integer=44100,
framesPerBuffer::Integer=2048, framesPerBuffer::Integer=2048,
show_warnings::Bool=false, show_warnings::Bool=false,
sample_format::PaSampleFormat=paInt16) sample_format::PaSampleFormat=paInt16)
#=
Get device parameters needed for opening with portaudio
default is input as 44100/16bit int, same as CD audio type input
=#
require_portaudio_init() require_portaudio_init()
stream = Pa_OpenStream(device_index, channels, input, sample_format, stream = Pa_OpenStream(device_index, channels, input, sample_format,
Cdouble(sample_rate), Culong(framesPerBuffer)) Cdouble(sample_rate), Culong(framesPerBuffer))
@ -150,7 +151,7 @@ type Pa_AudioStream <: AudioStream
datatype = PaSampleFormat_to_T(sample_format) datatype = PaSampleFormat_to_T(sample_format)
sbuf = ones(datatype, framesPerBuffer) sbuf = ones(datatype, framesPerBuffer)
this = new(root, DeviceInfo(sample_rate, framesPerBuffer), this = new(root, DeviceInfo(sample_rate, framesPerBuffer),
show_warnings, stream, sample_format, sbuf, false) show_warnings, stream, sample_format, sbuf, 0, false)
info("Scheduling PortAudio Render Task...") info("Scheduling PortAudio Render Task...")
if input if input
@schedule(pa_input_task(this)) @schedule(pa_input_task(this))
@ -161,6 +162,45 @@ type Pa_AudioStream <: AudioStream
end end
end end
"""
Blocking read from a Pa_AudioStream that is open as input
"""
function read_Pa_AudioStream(stream::Pa_AudioStream)
while true
while stream.parent_may_use_buffer == false
sleep(0.001)
end
buffer = deepcopy(stream.sbuffer)
stream.parent_may_use_buffer = false
return buffer
end
end
"""
Blocking write to a Pa_AudioStream that is open for output
"""
function write_Pa_AudioStream(stream::Pa_AudioStream, buffer)
retval = 1
sbufsize = length(stream.sbuffer)
inputlen = length(buffer)
if(inputlen > sbufsize)
info("Overflow at write_Pa_AudioStream")
retval = 0
elseif(inputlen < sbufsize)
info("Underflow at write_Pa_AudioStream")
retval = -1
end
while true
while stream.parent_may_use_buffer == false
sleep(0.001)
end
for idx in 1:min(sbufsize, inputlen)
stream.sbuffer[idx] = buffer[idx]
end
stream.parent_may_use_buffer = false
end
retval
end
############ Internal Functions ############ ############ Internal Functions ############
@ -193,11 +233,11 @@ function portaudio_task(stream::PortAudioStream)
end end
end end
function PaSampleFormat_to_T(fmt::PaSampleFormat) """
#=
Helper function to make the right type of buffer for various Helper function to make the right type of buffer for various
sample formats. Converts PaSampleFormat to a typeof sample formats. Converts PaSampleFormat to a typeof
=# """
function PaSampleFormat_to_T(fmt::PaSampleFormat)
retval = UInt8(0x0) retval = UInt8(0x0)
if fmt == 1 if fmt == 1
retval = Float32(1.0) retval = Float32(1.0)
@ -217,29 +257,29 @@ function PaSampleFormat_to_T(fmt::PaSampleFormat)
typeof(retval) typeof(retval)
end end
function pa_input_task(strm::Pa_AudioStream) """
#=
Get input device data, pass as a producer, no rendering Get input device data, pass as a producer, no rendering
=# """
function pa_input_task(stream::Pa_AudioStream)
info("PortAudio Input Task Running...") info("PortAudio Input Task Running...")
n = bufsize(strm) n = bufsize(stream)
datatype = PaSampleFormat_to_T(strm.sformat) datatype = PaSampleFormat_to_T(stream.sformat)
# bigger ccall buffer to avoid overflow related errorss # bigger ccall buffer to avoid overflow related errors
buffer = zeros(datatype, n * 8) buffer = zeros(datatype, n * 8)
try try
while true while true
while Pa_GetStreamReadAvailable(strm.stream) < n while Pa_GetStreamReadAvailable(stream.stream) < n
sleep(0.005) sleep(0.005)
end end
while strm.parent_may_use_buffer while stream.parent_may_use_buffer
sleep(0.005) sleep(0.005)
end end
err = ccall((:Pa_ReadStream, libportaudio), PaError, err = ccall((:Pa_ReadStream, libportaudio), PaError,
(Ptr{PaStream}, Ptr{Void}, Culong), (PaStream, Ptr{Void}, Culong),
strm.stream, buffer, n) stream.stream, buffer, n)
handle_status(err, strm.show_warnings) handle_status(err, stream.show_warnings)
strm.sbuffer[1: n] = buffer[1: n] stream.sbuffer[1: n] = buffer[1: n]
strm.parent_may_use_buffer = true stream.parent_may_use_buffer = true
sleep(0.005) sleep(0.005)
end end
catch ex catch ex
@ -248,20 +288,27 @@ function pa_input_task(strm::Pa_AudioStream)
end end
end end
function pa_output_task(stream::Pa_AudioStream) """
#=
Send output device data, no rendering Send output device data, no rendering
=# """
function pa_output_task(stream::Pa_AudioStream)
info("PortAudio Output Task Running...") info("PortAudio Output Task Running...")
n = bufsize(stream) n = bufsize(stream)
datatype = PaSampleFormat_to_T(stream.sformat)
buffer = zeros(datatype, n)
try try
while true while true
while Pa_GetStreamWriteAvailable(stream.stream) < n navail = stream.sbuffer_output_waiting
if navail > n
info("Possible output buffer overflow in stream")
navail = n
end
if (navail > 1) & (stream.parent_may_use_buffer == false) &
(Pa_GetStreamWriteAvailable(stream.stream) < navail)
Pa_WriteStream(stream.stream, stream.sbuffer,
navail, stream.show_warnings)
stream.parent_may_use_buffer = true
else
sleep(0.005) sleep(0.005)
end end
Pa_WriteStream(stream.stream, buffer, n, stream.show_warnings)
end end
catch ex catch ex
warn("Audio Output Task died with exception: $ex") warn("Audio Output Task died with exception: $ex")
@ -291,21 +338,23 @@ type PaHostApiInfo
defaultOutputDevice::PaDeviceIndex defaultOutputDevice::PaDeviceIndex
end end
type PortAudioInterface <: AudioInterface @compat type PortAudioInterface <: AudioInterface
name::String name::AbstractString
host_api::String host_api::AbstractString
max_input_channels::Int max_input_channels::Int
max_output_channels::Int max_output_channels::Int
device_index::PaDeviceIndex
end end
function get_portaudio_devices() function get_portaudio_devices()
require_portaudio_init() require_portaudio_init()
device_count = ccall((:Pa_GetDeviceCount, libportaudio), PaDeviceIndex, ()) device_count = ccall((:Pa_GetDeviceCount, libportaudio), PaDeviceIndex, ())
pa_devices = [Pa_GetDeviceInfo(i) for i in 0:(device_count - 1)] pa_devices = [ [Pa_GetDeviceInfo(i), i] for i in 0:(device_count - 1)]
[PortAudioInterface(bytestring(d.name), [PortAudioInterface(bytestring(d[1].name),
bytestring(Pa_GetHostApiInfo(d.host_api).name), bytestring(Pa_GetHostApiInfo(d[1].host_api).name),
d.max_input_channels, d[1].max_input_channels,
d.max_output_channels) d[1].max_output_channels,
d[2])
for d in pa_devices] for d in pa_devices]
end end