mostly adding tests and fixing bugs. passing tests now

This commit is contained in:
Spencer Russell 2017-05-11 00:58:49 -04:00
parent 9e3e66d37a
commit 0c36e1eec5
4 changed files with 293 additions and 132 deletions

23
deps/src/pa_shim.c vendored
View file

@ -60,35 +60,44 @@ int pa_shim_processcb(const void *input, void *output,
if(info->notifycb == NULL) { if(info->notifycb == NULL) {
fprintf(stderr, "pa_shim ERROR: notifycb is NULL\n"); fprintf(stderr, "pa_shim ERROR: notifycb is NULL\n");
} }
int nwrite;
int nwrite = PaUtil_GetRingBufferWriteAvailable(info->inputbuf); if(info->inputbuf) {
int nread = PaUtil_GetRingBufferReadAvailable(info->outputbuf); nwrite = PaUtil_GetRingBufferWriteAvailable(info->inputbuf);
nwrite = MIN(frameCount, nwrite); nwrite = MIN(frameCount, nwrite);
}
int nread;
if(info->outputbuf) {
nread = PaUtil_GetRingBufferReadAvailable(info->outputbuf);
nread = MIN(frameCount, nread); nread = MIN(frameCount, nread);
if(info->sync) { }
if(info->inputbuf && info->outputbuf && info->sync) {
// to keep the buffers synchronized, set readable and writable to // to keep the buffers synchronized, set readable and writable to
// their minimum value // their minimum value
nread = MIN(nread, nwrite); nread = MIN(nread, nwrite);
nwrite = nread; nwrite = nread;
} }
// read/write from the ringbuffers // read/write from the ringbuffers
if(info->inputbuf) {
PaUtil_WriteRingBuffer(info->inputbuf, input, nwrite); PaUtil_WriteRingBuffer(info->inputbuf, input, nwrite);
if(info->notifycb) { if(info->notifycb) {
info->notifycb(info->inputhandle); info->notifycb(info->inputhandle);
} }
if(nwrite < frameCount) {
senderr(info, PA_SHIM_ERRMSG_OVERFLOW);
}
}
if(info->outputbuf) {
PaUtil_ReadRingBuffer(info->outputbuf, output, nread); PaUtil_ReadRingBuffer(info->outputbuf, output, nread);
if(info->notifycb) { if(info->notifycb) {
info->notifycb(info->outputhandle); info->notifycb(info->outputhandle);
} }
if(nwrite < frameCount) {
senderr(info, PA_SHIM_ERRMSG_OVERFLOW);
}
if(nread < frameCount) { if(nread < frameCount) {
senderr(info, PA_SHIM_ERRMSG_UNDERFLOW); senderr(info, PA_SHIM_ERRMSG_UNDERFLOW);
// we didn't fill the whole output buffer, so zero it out // we didn't fill the whole output buffer, so zero it out
memset(output+nread*info->outputbuf->elementSizeBytes, 0, memset(output+nread*info->outputbuf->elementSizeBytes, 0,
(frameCount - nread)*info->outputbuf->elementSizeBytes); (frameCount - nread)*info->outputbuf->elementSizeBytes);
} }
}
return paContinue; return paContinue;
} }

View file

@ -8,6 +8,10 @@ using Suppressor
using Base: AsyncCondition using Base: AsyncCondition
import Base: eltype, show
import Base: close, isopen
import Base: read, read!, write, flush
# Get binary dependencies loaded from BinDeps # Get binary dependencies loaded from BinDeps
include("../deps/deps.jl") include("../deps/deps.jl")
include("pa_shim.jl") include("pa_shim.jl")
@ -20,7 +24,6 @@ function __init__()
@suppress_err Pa_Initialize() @suppress_err Pa_Initialize()
end end
export PortAudioStream export PortAudioStream
# These sizes are all in frames # These sizes are all in frames
@ -41,7 +44,7 @@ const ERR_BUFSIZE=512
function versioninfo(io::IO=STDOUT) function versioninfo(io::IO=STDOUT)
println(io, Pa_GetVersionText()) println(io, Pa_GetVersionText())
println(io, "Version: ", Pa_GetVersion()) println(io, "Version: ", Pa_GetVersion())
println(io, "Shim Version: ", shimversion()) println(io, "Shim Source Hash: ", shimhash()[1:10])
end end
type PortAudioDevice type PortAudioDevice
@ -104,13 +107,15 @@ type PortAudioStream{T}
# we've got a synchronized duplex stream. initialize with the output buffer full # we've got a synchronized duplex stream. initialize with the output buffer full
write(this.sink, SampleBuf(zeros(T, blocksize*2, outchans), sr)) write(this.sink, SampleBuf(zeros(T, blocksize*2, outchans), sr))
end end
this.bufinfo = pa_shim_info_t(bufpointer(this.source), # pass NULL for input/output we're not using
bufpointer(this.sink), this.bufinfo = pa_shim_info_t(
inchans > 0 ? bufpointer(this.source) : C_NULL,
outchans > 0 ? bufpointer(this.sink) : C_NULL,
pointer(this.errbuf), pointer(this.errbuf),
synced, notifycb_c, synced, notifycb_c,
getnotifyhandle(this.sink), inchans > 0 ? notifyhandle(this.source) : C_NULL,
getnotifyhandle(this.source), outchans > 0 ? notifyhandle(this.sink) : C_NULL,
getnotifyhandle(this.errbuf)) notifyhandle(this.errbuf))
this.stream = @suppress_err Pa_OpenStream(inparams, outparams, this.stream = @suppress_err Pa_OpenStream(inparams, outparams,
float(sr), blocksize, float(sr), blocksize,
paNoFlag, shim_processcb_c, paNoFlag, shim_processcb_c,
@ -184,7 +189,7 @@ function PortAudioStream(inchans=2, outchans=2; kwargs...)
PortAudioStream(indevice, outdevice, inchans, outchans; kwargs...) PortAudioStream(indevice, outdevice, inchans, outchans; kwargs...)
end end
function Base.close(stream::PortAudioStream) function close(stream::PortAudioStream)
if stream.stream != C_NULL if stream.stream != C_NULL
Pa_StopStream(stream.stream) Pa_StopStream(stream.stream)
Pa_CloseStream(stream.stream) Pa_CloseStream(stream.stream)
@ -196,18 +201,18 @@ function Base.close(stream::PortAudioStream)
nothing nothing
end end
Base.isopen(stream::PortAudioStream) = stream.stream != C_NULL isopen(stream::PortAudioStream) = stream.stream != C_NULL
SampledSignals.samplerate(stream::PortAudioStream) = stream.samplerate SampledSignals.samplerate(stream::PortAudioStream) = stream.samplerate
Base.eltype{T}(stream::PortAudioStream{T}) = T eltype{T}(stream::PortAudioStream{T}) = T
Base.read(stream::PortAudioStream, args...) = read(stream.source, args...) read(stream::PortAudioStream, args...) = read(stream.source, args...)
Base.read!(stream::PortAudioStream, args...) = read!(stream.source, args...) read!(stream::PortAudioStream, args...) = read!(stream.source, args...)
Base.write(stream::PortAudioStream, args...) = write(stream.sink, args...) write(stream::PortAudioStream, args...) = write(stream.sink, args...)
Base.write(sink::PortAudioStream, source::PortAudioStream, args...) = write(sink.sink, source.source, args...) write(sink::PortAudioStream, source::PortAudioStream, args...) = write(sink.sink, source.source, args...)
Base.flush(stream::PortAudioStream) = flush(stream.sink) flush(stream::PortAudioStream) = flush(stream.sink)
function Base.show(io::IO, stream::PortAudioStream) function show(io::IO, stream::PortAudioStream)
println(io, typeof(stream)) println(io, typeof(stream))
println(io, " Samplerate: ", samplerate(stream), "Hz") println(io, " Samplerate: ", samplerate(stream), "Hz")
print(io, " Buffer Size: ", stream.blocksize, " frames") print(io, " Buffer Size: ", stream.blocksize, " frames")
@ -272,23 +277,19 @@ end
SampledSignals.nchannels(s::Union{PortAudioSink, PortAudioSource}) = s.nchannels SampledSignals.nchannels(s::Union{PortAudioSink, PortAudioSource}) = s.nchannels
SampledSignals.samplerate(s::Union{PortAudioSink, PortAudioSource}) = samplerate(s.stream) SampledSignals.samplerate(s::Union{PortAudioSink, PortAudioSource}) = samplerate(s.stream)
SampledSignals.blocksize(s::Union{PortAudioSink, PortAudioSource}) = s.stream.blocksize SampledSignals.blocksize(s::Union{PortAudioSink, PortAudioSource}) = s.stream.blocksize
Base.eltype(::Union{PortAudioSink{T}, PortAudioSource{T}}) where {T} = T eltype(::Union{PortAudioSink{T}, PortAudioSource{T}}) where {T} = T
Base.close(s::Union{PortAudioSink, PortAudioSource}) = close(s.ringbuf) close(s::Union{PortAudioSink, PortAudioSource}) = close(s.ringbuf)
Base.isopen(s::Union{PortAudioSink, PortAudioSource}) = isopen(s.ringbuf) isopen(s::Union{PortAudioSink, PortAudioSource}) = isopen(s.ringbuf)
RingBuffers.getnotifyhandle(s::Union{PortAudioSink, PortAudioSource}) = getnotifyhandle(s.ringbuf) RingBuffers.notifyhandle(s::Union{PortAudioSink, PortAudioSource}) = notifyhandle(s.ringbuf)
bufpointer(s::Union{PortAudioSink, PortAudioSource}) = pointer(s.ringbuf) bufpointer(s::Union{PortAudioSink, PortAudioSource}) = pointer(s.ringbuf)
name(s::Union{PortAudioSink, PortAudioSource}) = s.name name(s::Union{PortAudioSink, PortAudioSource}) = s.name
function Base.show(io::IO, stream::T) where {T <: Union{PortAudioSink, PortAudioSource}} function show(io::IO, stream::T) where {T <: Union{PortAudioSink, PortAudioSource}}
println(io, T, "(\"", stream.name, "\")") println(io, T, "(\"", stream.name, "\")")
print(io, nchannels(stream), " channels") print(io, nchannels(stream), " channels")
end end
# function Base.flush(sink::PortAudioSink) flush(sink::PortAudioSink) = flush(sink.ringbuf)
# while nwritable(sink.ringbuf) < length(sink.ringbuf)
# wait(sink.ringbuf)
# end
# end
function SampledSignals.unsafe_write(sink::PortAudioSink, buf::Array, frameoffset, framecount) function SampledSignals.unsafe_write(sink::PortAudioSink, buf::Array, frameoffset, framecount)
nwritten = 0 nwritten = 0

View file

@ -240,11 +240,11 @@ function handle_status(err::PaError, show_warnings::Bool=true)
if show_warnings if show_warnings
msg = ccall((:Pa_GetErrorText, libportaudio), msg = ccall((:Pa_GetErrorText, libportaudio),
Ptr{Cchar}, (PaError,), err) Ptr{Cchar}, (PaError,), err)
warn("libportaudio: " * bytestring(msg)) warn("libportaudio: " * unsafe_string(msg))
end end
elseif err != PA_NO_ERROR elseif err != PA_NO_ERROR
msg = ccall((:Pa_GetErrorText, libportaudio), msg = ccall((:Pa_GetErrorText, libportaudio),
Ptr{Cchar}, (PaError,), err) Ptr{Cchar}, (PaError,), err)
error("libportaudio: " * bytestring(msg)) error("libportaudio: " * unsafe_string(msg))
end end
end end

View file

@ -1,86 +1,193 @@
#!/usr/bin/env julia #!/usr/bin/env julia
if VERSION >= v"0.5.0-dev+7720" using Base.Test
using Base.Test using TestSetExtensions
else
using BaseTestNext
end
using PortAudio using PortAudio
using SampledSignals using SampledSignals
using RingBuffers using RingBuffers
function test_callback(inchans, outchans) # pull in some extra stuff we need to test the callback directly
nframes = Culong(8) using PortAudio: notifyhandle, notifycb_c, shim_processcb_c
using PortAudio: pa_shim_errmsg_t, pa_shim_info_t
using PortAudio: PA_SHIM_ERRMSG_ERR_OVERFLOW, PA_SHIM_ERRMSG_UNDERFLOW, PA_SHIM_ERRMSG_OVERFLOW
cb = PortAudio.pa_callbacks[Float32] "Setup buffers to test callback behavior"
inbuf = rand(Float32, inchans*nframes) # simulate microphone input function setup_callback(inchans, outchans, nframes, synced)
sourcebuf = LockFreeRingBuffer(Float32, inchans*nframes*8) # the microphone input should end up here sourcebuf = RingBuffer{Float32}(inchans, nframes*2) # the microphone input should end up here
sinkbuf = RingBuffer{Float32}(outchans, nframes*2) # the callback should copy this to cb_output
errbuf = RingBuffer{pa_shim_errmsg_t}(1, 8)
outbuf = zeros(Float32, outchans*nframes) # this is where the output should go # pass NULL for i/o we're not using
sinkbuf = LockFreeRingBuffer(Float32, outchans*nframes*8) # the callback should copy this to outbuf info = pa_shim_info_t(
inchans > 0 ? pointer(sourcebuf) : C_NULL,
# 2 input channels, 3 output channels outchans > 0 ? pointer(sinkbuf) : C_NULL,
info = PortAudio.CallbackInfo(inchans, sourcebuf, outchans, sinkbuf, true) pointer(errbuf),
synced, notifycb_c,
# handle any conversions here so they don't mess with the allocation inchans > 0 ? notifyhandle(sourcebuf) : C_NULL,
# the seemingly-redundant type specifiers avoid some allocation during the ccall. outchans > 0 ? notifyhandle(sinkbuf) : C_NULL,
# might be due to https://github.com/JuliaLang/julia/issues/15276 notifyhandle(errbuf)
inptr::Ptr{Float32} = Ptr{Float32}(pointer(inbuf)) )
outptr::Ptr{Float32} = Ptr{Float32}(pointer(outbuf))
flags = Culong(0) flags = Culong(0)
infoptr::Ptr{PortAudio.CallbackInfo{Float32}} = Ptr{PortAudio.CallbackInfo{Float32}}(pointer_from_objref(info))
testin = zeros(Float32, inchans*nframes) cb_input = rand(Float32, inchans, nframes) # simulate microphone input
testout = rand(Float32, outchans*nframes) cb_output = rand(Float32, outchans, nframes) # this is where the output should go
write(sinkbuf, testout) # fill the output ringbuffer
ret = ccall(cb, Cint,
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{PortAudio.CallbackInfo{Float32}}),
inptr, outptr, nframes, C_NULL, flags, infoptr)
@test ret === PortAudio.paContinue
@test outbuf == testout
read!(sourcebuf, testin)
@test inbuf == testin
function processfunc()
ccall(shim_processcb_c, Cint,
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{Void}),
cb_input, cb_output, nframes, C_NULL, flags, pointer_from_objref(info))
end
(sourcebuf, sinkbuf, errbuf, cb_input, cb_output, processfunc)
end
function test_callback(inchans, outchans, synced)
nframes = 8
(sourcebuf, sinkbuf, errbuf,
cb_input, cb_output, process) = setup_callback(inchans, outchans,
nframes, synced)
if outchans > 0 if outchans > 0
underfill = 3 # should be less than nframes testout = rand(Float32, outchans, nframes) # generate some test data to play
testout = rand(Float32, outchans*underfill)
write(sinkbuf, testout) # underfill the output ringbuffer
# call again (partial underrun)
ret = ccall(cb, Cint,
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{PortAudio.CallbackInfo{Float32}}),
inptr, outptr, nframes, C_NULL, flags, infoptr)
@test ret === PortAudio.paContinue
@test outbuf[1:outchans*underfill] == testout
@test outbuf[outchans*underfill+1:outchans*nframes] == zeros(Float32, (nframes-underfill)*outchans)
@test nreadable(sourcebuf) == inchans*underfill
@test read!(sourcebuf, testin) == inchans*underfill
@test testin[1:inchans*underfill] == inbuf[1:inchans*underfill]
# call again (total underrun)
ret = ccall(cb, Cint,
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{PortAudio.CallbackInfo{Float32}}),
inptr, outptr, nframes, C_NULL, flags, infoptr)
@test ret === PortAudio.paContinue
@test outbuf == zeros(Float32, outchans*nframes)
@test nreadable(sourcebuf) == 0
write(sinkbuf, testout) # fill the output ringbuffer write(sinkbuf, testout) # fill the output ringbuffer
# test allocation end
alloc = @allocated ccall(cb, Cint, @test process() == PortAudio.paContinue
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{PortAudio.CallbackInfo{Float32}}), if outchans > 0
inptr, outptr, nframes, C_NULL, flags, infoptr) # testout -> sinkbuf -> cb_output
@test alloc == 0 @test cb_output == testout
# now test allocation in underrun state end
alloc = @allocated ccall(cb, Cint, if inchans > 0
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{PortAudio.CallbackInfo{Float32}}), # cb_input -> sourcebuf
inptr, outptr, nframes, C_NULL, flags, infoptr) @test read(sourcebuf, nframes) == cb_input
@test alloc == 0 end
@test framesreadable(errbuf) == 0
end
"""
test_callback_underflow(inchans, outchans; nframes=8, underfill=3, synced=false)
Test that the callback works on underflow conditions. underfill is the numer of
frames we feed in, which should be less than nframes.
"""
function test_callback_underflow(inchans, outchans, synced)
nframes = 8
underfill = 3 # must be less than nframes
(sourcebuf, sinkbuf, errbuf,
cb_input, cb_output, process) = setup_callback(inchans, outchans,
nframes, synced)
outchans > 0 || error("Can't test underflow with no output")
testout = rand(Float32, outchans, underfill)
write(sinkbuf, testout) # underfill the output ringbuffer
# call callback (partial underflow)
@test process() == PortAudio.paContinue
@test cb_output[:, 1:underfill] == testout
@test cb_output[:, (underfill+1):nframes] == zeros(Float32, outchans, (nframes-underfill))
errs = readavailable(errbuf)
if inchans > 0
received = readavailable(sourcebuf)
if synced
@test size(received, 2) == underfill
@test received == cb_input[:, 1:underfill]
@test length(errs) == 2
@test Set(errs) == Set([PA_SHIM_ERRMSG_UNDERFLOW, PA_SHIM_ERRMSG_OVERFLOW])
else
@test size(received, 2) == nframes
@test received == cb_input
@test length(errs) == 1
@test errs[1] == PA_SHIM_ERRMSG_UNDERFLOW
end
else
@test length(errs) == 1
@test errs[1] == PA_SHIM_ERRMSG_UNDERFLOW
end
# call again (total underflow)
@test process() == PortAudio.paContinue
@test cb_output == zeros(Float32, outchans, nframes)
errs = readavailable(errbuf)
if inchans > 0
received = readavailable(sourcebuf)
if synced
@test size(received, 2) == 0
@test length(errs) == 2
@test Set(errs) == Set([PA_SHIM_ERRMSG_UNDERFLOW, PA_SHIM_ERRMSG_OVERFLOW])
else
@test size(received, 2) == nframes
@test received == cb_input
@test length(errs) == 1
@test errs[1] == PA_SHIM_ERRMSG_UNDERFLOW
end
else
@test length(errs) == 1
@test errs[1] == PA_SHIM_ERRMSG_UNDERFLOW
end
end
function test_callback_overflow(inchans, outchans, synced)
nframes = 8
(sourcebuf, sinkbuf, errbuf,
cb_input, cb_output, process) = setup_callback(inchans, outchans,
nframes, synced)
inchans > 0 || error("Can't test overflow with no input")
@test frameswritable(sinkbuf) == nframes*2
# the first time it should half-fill the input ring buffer
if outchans > 0
testout = rand(Float32, outchans, nframes)
write(sinkbuf, testout)
end
@test framesreadable(sourcebuf) == 0
outchans > 0 && @test frameswritable(sinkbuf) == nframes
@test process() == PortAudio.paContinue
@test framesreadable(errbuf) == 0
@test framesreadable(sourcebuf) == nframes
outchans > 0 && @test frameswritable(sinkbuf) == nframes*2
# now run the process func again to completely fill the input ring buffer
outchans > 0 && write(sinkbuf, testout)
@test framesreadable(sourcebuf) == nframes
outchans > 0 && @test frameswritable(sinkbuf) == nframes
@test process() == PortAudio.paContinue
@test framesreadable(errbuf) == 0
@test framesreadable(sourcebuf) == nframes*2
outchans > 0 && @test frameswritable(sinkbuf) == nframes*2
# now this time the process func should overflow the input buffer
outchans > 0 && write(sinkbuf, testout)
@test framesreadable(sourcebuf) == nframes*2
outchans > 0 && @test frameswritable(sinkbuf) == nframes
@test process() == PortAudio.paContinue
@test framesreadable(sourcebuf) == nframes*2
errs = readavailable(errbuf)
if outchans > 0
if synced
# if input and output are synced, thec callback didn't pull from
# the output ringbuf
@test frameswritable(sinkbuf) == nframes
@test cb_output == zeros(Float32, outchans, nframes)
@test length(errs) == 2
@test Set(errs) == Set([PA_SHIM_ERRMSG_UNDERFLOW, PA_SHIM_ERRMSG_OVERFLOW])
else
@test frameswritable(sinkbuf) == nframes*2
@test length(errs) == 1
@test errs[1] == PA_SHIM_ERRMSG_OVERFLOW
end
else
@test length(errs) == 1
@test errs[1] == PA_SHIM_ERRMSG_OVERFLOW
end end
end end
# these test are currently set up to run on OSX # these test are currently set up to run on OSX
@testset "PortAudio Tests" begin @testset DottedTestSet "PortAudio Tests" begin
if is_windows()
default_indev = "Microphone Array (Realtek High "
default_outdev = "Speaker/Headphone (Realtek High"
elseif is_apple()
default_indev = "Built-in Microph"
default_outdev = "Built-in Output"
end
devs = PortAudio.devices() devs = PortAudio.devices()
i = findfirst(d -> d.maxinchans > 0, devs) i = findfirst(d -> d.maxinchans > 0, devs)
indev = i > 0 ? devs[i] : nothing indev = i > 0 ? devs[i] : nothing
@ -92,24 +199,68 @@ end
@testset "Reports version" begin @testset "Reports version" begin
io = IOBuffer() io = IOBuffer()
PortAudio.versioninfo(io) PortAudio.versioninfo(io)
result = takebuf_string(io) result = split(String(take!((io))), "\n")
# make sure this is the same version I tested with # make sure this is the same version I tested with
@test result == @test startswith(result[1], "PortAudio V19-devel")
"""PortAudio V19-devel (built Aug 6 2014 17:54:39) @test result[2] == "Version: 1899"
Version Number: 1899 @test result[3] == "Shim Source Hash: 4bfafb6888"
"""
end end
@testset "PortAudio Callback works for duplex stream" begin @testset "Basic callback functionality" begin
test_callback(2, 3) @testset "basic duplex (no sync)" begin
test_callback(2, 3, false)
end
@testset "basic input-only (no sync)" begin
test_callback(2, 0, false)
end
@testset "basic output-only (no sync)" begin
test_callback(0, 2, false)
end
@testset "basic no input or output (no sync)" begin
test_callback(0, 0, false)
end
@testset "basic duplex (sync)" begin
test_callback(2, 3, true)
end
@testset "basic input-only (sync)" begin
test_callback(2, 0, true)
end
@testset "basic output-only (sync)" begin
test_callback(0, 2, true)
end
@testset "basic no input or output (sync)" begin
test_callback(0, 0, true)
end
end end
@testset "Callback works with input-only stream" begin @testset "Ouput underflow" begin
test_callback(2, 0) @testset "underflow duplex (nosync)" begin
test_callback_underflow(2, 3, false)
end
@testset "underflow output-only (nosync)" begin
test_callback_underflow(0, 3, false)
end
@testset "underflow duplex (sync)" begin
test_callback_underflow(2, 3, true)
end
@testset "underflow output-only (sync)" begin
test_callback_underflow(0, 3, true)
end
end end
@testset "Callback works with output-only stream" begin @testset "Input overflow" begin
test_callback(0, 2) @testset "overflow duplex (nosync)" begin
test_callback_overflow(2, 3, false)
end
@testset "overflow input-only (nosync)" begin
test_callback_overflow(2, 0, false)
end
@testset "overflow duplex (sync)" begin
test_callback_overflow(2, 3, true)
end
@testset "overflow input-only (sync)" begin
test_callback_overflow(2, 0, true)
end
end end
@testset "Open Default Device" begin @testset "Open Default Device" begin
@ -139,18 +290,18 @@ end
close(stream) close(stream)
end end
@testset "Open Device by name" begin @testset "Open Device by name" begin
stream = PortAudioStream("Built-in Microph", "Built-in Output") stream = PortAudioStream(default_indev, default_outdev)
buf = read(stream, 0.001s) buf = read(stream, 0.001s)
@test size(buf) == (round(Int, 0.001 * samplerate(stream)), nchannels(stream.source)) @test size(buf) == (round(Int, 0.001 * samplerate(stream)), nchannels(stream.source))
write(stream, buf) write(stream, buf)
io = IOBuffer() io = IOBuffer()
show(io, stream) show(io, stream)
@test takebuf_string(io) == """ @test String(take!(io)) == """
PortAudio.PortAudioStream{Float32} PortAudio.PortAudioStream{Float32}
Samplerate: 48000.0Hz Samplerate: 44100.0Hz
Buffer Size: 4096 frames Buffer Size: 4096 frames
2 channel sink: "Built-in Output" 2 channel sink: "$default_outdev"
2 channel source: "Built-in Microph\"""" 2 channel source: "$default_indev\""""
close(stream) close(stream)
end end
@testset "Error on wrong name" begin @testset "Error on wrong name" begin