mostly adding tests and fixing bugs. passing tests now
This commit is contained in:
parent
9e3e66d37a
commit
0c36e1eec5
4 changed files with 293 additions and 132 deletions
51
deps/src/pa_shim.c
vendored
51
deps/src/pa_shim.c
vendored
|
@ -60,34 +60,43 @@ int pa_shim_processcb(const void *input, void *output,
|
|||
if(info->notifycb == NULL) {
|
||||
fprintf(stderr, "pa_shim ERROR: notifycb is NULL\n");
|
||||
}
|
||||
|
||||
int nwrite = PaUtil_GetRingBufferWriteAvailable(info->inputbuf);
|
||||
int nread = PaUtil_GetRingBufferReadAvailable(info->outputbuf);
|
||||
nwrite = MIN(frameCount, nwrite);
|
||||
nread = MIN(frameCount, nread);
|
||||
if(info->sync) {
|
||||
int nwrite;
|
||||
if(info->inputbuf) {
|
||||
nwrite = PaUtil_GetRingBufferWriteAvailable(info->inputbuf);
|
||||
nwrite = MIN(frameCount, nwrite);
|
||||
}
|
||||
int nread;
|
||||
if(info->outputbuf) {
|
||||
nread = PaUtil_GetRingBufferReadAvailable(info->outputbuf);
|
||||
nread = MIN(frameCount, nread);
|
||||
}
|
||||
if(info->inputbuf && info->outputbuf && info->sync) {
|
||||
// to keep the buffers synchronized, set readable and writable to
|
||||
// their minimum value
|
||||
nread = MIN(nread, nwrite);
|
||||
nwrite = nread;
|
||||
}
|
||||
// read/write from the ringbuffers
|
||||
PaUtil_WriteRingBuffer(info->inputbuf, input, nwrite);
|
||||
if(info->notifycb) {
|
||||
info->notifycb(info->inputhandle);
|
||||
if(info->inputbuf) {
|
||||
PaUtil_WriteRingBuffer(info->inputbuf, input, nwrite);
|
||||
if(info->notifycb) {
|
||||
info->notifycb(info->inputhandle);
|
||||
}
|
||||
if(nwrite < frameCount) {
|
||||
senderr(info, PA_SHIM_ERRMSG_OVERFLOW);
|
||||
}
|
||||
}
|
||||
PaUtil_ReadRingBuffer(info->outputbuf, output, nread);
|
||||
if(info->notifycb) {
|
||||
info->notifycb(info->outputhandle);
|
||||
}
|
||||
if(nwrite < frameCount) {
|
||||
senderr(info, PA_SHIM_ERRMSG_OVERFLOW);
|
||||
}
|
||||
if(nread < frameCount) {
|
||||
senderr(info, PA_SHIM_ERRMSG_UNDERFLOW);
|
||||
// we didn't fill the whole output buffer, so zero it out
|
||||
memset(output+nread*info->outputbuf->elementSizeBytes, 0,
|
||||
(frameCount - nread)*info->outputbuf->elementSizeBytes);
|
||||
if(info->outputbuf) {
|
||||
PaUtil_ReadRingBuffer(info->outputbuf, output, nread);
|
||||
if(info->notifycb) {
|
||||
info->notifycb(info->outputhandle);
|
||||
}
|
||||
if(nread < frameCount) {
|
||||
senderr(info, PA_SHIM_ERRMSG_UNDERFLOW);
|
||||
// we didn't fill the whole output buffer, so zero it out
|
||||
memset(output+nread*info->outputbuf->elementSizeBytes, 0,
|
||||
(frameCount - nread)*info->outputbuf->elementSizeBytes);
|
||||
}
|
||||
}
|
||||
|
||||
return paContinue;
|
||||
|
|
|
@ -8,6 +8,10 @@ using Suppressor
|
|||
|
||||
using Base: AsyncCondition
|
||||
|
||||
import Base: eltype, show
|
||||
import Base: close, isopen
|
||||
import Base: read, read!, write, flush
|
||||
|
||||
# Get binary dependencies loaded from BinDeps
|
||||
include("../deps/deps.jl")
|
||||
include("pa_shim.jl")
|
||||
|
@ -20,7 +24,6 @@ function __init__()
|
|||
@suppress_err Pa_Initialize()
|
||||
end
|
||||
|
||||
|
||||
export PortAudioStream
|
||||
|
||||
# These sizes are all in frames
|
||||
|
@ -41,7 +44,7 @@ const ERR_BUFSIZE=512
|
|||
function versioninfo(io::IO=STDOUT)
|
||||
println(io, Pa_GetVersionText())
|
||||
println(io, "Version: ", Pa_GetVersion())
|
||||
println(io, "Shim Version: ", shimversion())
|
||||
println(io, "Shim Source Hash: ", shimhash()[1:10])
|
||||
end
|
||||
|
||||
type PortAudioDevice
|
||||
|
@ -104,13 +107,15 @@ type PortAudioStream{T}
|
|||
# we've got a synchronized duplex stream. initialize with the output buffer full
|
||||
write(this.sink, SampleBuf(zeros(T, blocksize*2, outchans), sr))
|
||||
end
|
||||
this.bufinfo = pa_shim_info_t(bufpointer(this.source),
|
||||
bufpointer(this.sink),
|
||||
pointer(this.errbuf),
|
||||
synced, notifycb_c,
|
||||
getnotifyhandle(this.sink),
|
||||
getnotifyhandle(this.source),
|
||||
getnotifyhandle(this.errbuf))
|
||||
# pass NULL for input/output we're not using
|
||||
this.bufinfo = pa_shim_info_t(
|
||||
inchans > 0 ? bufpointer(this.source) : C_NULL,
|
||||
outchans > 0 ? bufpointer(this.sink) : C_NULL,
|
||||
pointer(this.errbuf),
|
||||
synced, notifycb_c,
|
||||
inchans > 0 ? notifyhandle(this.source) : C_NULL,
|
||||
outchans > 0 ? notifyhandle(this.sink) : C_NULL,
|
||||
notifyhandle(this.errbuf))
|
||||
this.stream = @suppress_err Pa_OpenStream(inparams, outparams,
|
||||
float(sr), blocksize,
|
||||
paNoFlag, shim_processcb_c,
|
||||
|
@ -184,7 +189,7 @@ function PortAudioStream(inchans=2, outchans=2; kwargs...)
|
|||
PortAudioStream(indevice, outdevice, inchans, outchans; kwargs...)
|
||||
end
|
||||
|
||||
function Base.close(stream::PortAudioStream)
|
||||
function close(stream::PortAudioStream)
|
||||
if stream.stream != C_NULL
|
||||
Pa_StopStream(stream.stream)
|
||||
Pa_CloseStream(stream.stream)
|
||||
|
@ -196,18 +201,18 @@ function Base.close(stream::PortAudioStream)
|
|||
nothing
|
||||
end
|
||||
|
||||
Base.isopen(stream::PortAudioStream) = stream.stream != C_NULL
|
||||
isopen(stream::PortAudioStream) = stream.stream != C_NULL
|
||||
|
||||
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...)
|
||||
Base.read!(stream::PortAudioStream, args...) = read!(stream.source, args...)
|
||||
Base.write(stream::PortAudioStream, args...) = write(stream.sink, args...)
|
||||
Base.write(sink::PortAudioStream, source::PortAudioStream, args...) = write(sink.sink, source.source, args...)
|
||||
Base.flush(stream::PortAudioStream) = flush(stream.sink)
|
||||
read(stream::PortAudioStream, args...) = read(stream.source, args...)
|
||||
read!(stream::PortAudioStream, args...) = read!(stream.source, args...)
|
||||
write(stream::PortAudioStream, args...) = write(stream.sink, args...)
|
||||
write(sink::PortAudioStream, source::PortAudioStream, args...) = write(sink.sink, source.source, args...)
|
||||
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, " Samplerate: ", samplerate(stream), "Hz")
|
||||
print(io, " Buffer Size: ", stream.blocksize, " frames")
|
||||
|
@ -272,23 +277,19 @@ end
|
|||
SampledSignals.nchannels(s::Union{PortAudioSink, PortAudioSource}) = s.nchannels
|
||||
SampledSignals.samplerate(s::Union{PortAudioSink, PortAudioSource}) = samplerate(s.stream)
|
||||
SampledSignals.blocksize(s::Union{PortAudioSink, PortAudioSource}) = s.stream.blocksize
|
||||
Base.eltype(::Union{PortAudioSink{T}, PortAudioSource{T}}) where {T} = T
|
||||
Base.close(s::Union{PortAudioSink, PortAudioSource}) = close(s.ringbuf)
|
||||
Base.isopen(s::Union{PortAudioSink, PortAudioSource}) = isopen(s.ringbuf)
|
||||
RingBuffers.getnotifyhandle(s::Union{PortAudioSink, PortAudioSource}) = getnotifyhandle(s.ringbuf)
|
||||
eltype(::Union{PortAudioSink{T}, PortAudioSource{T}}) where {T} = T
|
||||
close(s::Union{PortAudioSink, PortAudioSource}) = close(s.ringbuf)
|
||||
isopen(s::Union{PortAudioSink, PortAudioSource}) = isopen(s.ringbuf)
|
||||
RingBuffers.notifyhandle(s::Union{PortAudioSink, PortAudioSource}) = notifyhandle(s.ringbuf)
|
||||
bufpointer(s::Union{PortAudioSink, PortAudioSource}) = pointer(s.ringbuf)
|
||||
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, "\")")
|
||||
print(io, nchannels(stream), " channels")
|
||||
end
|
||||
|
||||
# function Base.flush(sink::PortAudioSink)
|
||||
# while nwritable(sink.ringbuf) < length(sink.ringbuf)
|
||||
# wait(sink.ringbuf)
|
||||
# end
|
||||
# end
|
||||
flush(sink::PortAudioSink) = flush(sink.ringbuf)
|
||||
|
||||
function SampledSignals.unsafe_write(sink::PortAudioSink, buf::Array, frameoffset, framecount)
|
||||
nwritten = 0
|
||||
|
|
|
@ -240,11 +240,11 @@ function handle_status(err::PaError, show_warnings::Bool=true)
|
|||
if show_warnings
|
||||
msg = ccall((:Pa_GetErrorText, libportaudio),
|
||||
Ptr{Cchar}, (PaError,), err)
|
||||
warn("libportaudio: " * bytestring(msg))
|
||||
warn("libportaudio: " * unsafe_string(msg))
|
||||
end
|
||||
elseif err != PA_NO_ERROR
|
||||
msg = ccall((:Pa_GetErrorText, libportaudio),
|
||||
Ptr{Cchar}, (PaError,), err)
|
||||
error("libportaudio: " * bytestring(msg))
|
||||
error("libportaudio: " * unsafe_string(msg))
|
||||
end
|
||||
end
|
||||
|
|
313
test/runtests.jl
313
test/runtests.jl
|
@ -1,86 +1,193 @@
|
|||
#!/usr/bin/env julia
|
||||
|
||||
if VERSION >= v"0.5.0-dev+7720"
|
||||
using Base.Test
|
||||
else
|
||||
using BaseTestNext
|
||||
end
|
||||
using Base.Test
|
||||
using TestSetExtensions
|
||||
using PortAudio
|
||||
using SampledSignals
|
||||
using RingBuffers
|
||||
|
||||
function test_callback(inchans, outchans)
|
||||
nframes = Culong(8)
|
||||
# pull in some extra stuff we need to test the callback directly
|
||||
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]
|
||||
inbuf = rand(Float32, inchans*nframes) # simulate microphone input
|
||||
sourcebuf = LockFreeRingBuffer(Float32, inchans*nframes*8) # the microphone input should end up here
|
||||
"Setup buffers to test callback behavior"
|
||||
function setup_callback(inchans, outchans, nframes, synced)
|
||||
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
|
||||
sinkbuf = LockFreeRingBuffer(Float32, outchans*nframes*8) # the callback should copy this to outbuf
|
||||
|
||||
# 2 input channels, 3 output channels
|
||||
info = PortAudio.CallbackInfo(inchans, sourcebuf, outchans, sinkbuf, true)
|
||||
|
||||
# handle any conversions here so they don't mess with the allocation
|
||||
# the seemingly-redundant type specifiers avoid some allocation during the ccall.
|
||||
# might be due to https://github.com/JuliaLang/julia/issues/15276
|
||||
inptr::Ptr{Float32} = Ptr{Float32}(pointer(inbuf))
|
||||
outptr::Ptr{Float32} = Ptr{Float32}(pointer(outbuf))
|
||||
# pass NULL for i/o we're not using
|
||||
info = pa_shim_info_t(
|
||||
inchans > 0 ? pointer(sourcebuf) : C_NULL,
|
||||
outchans > 0 ? pointer(sinkbuf) : C_NULL,
|
||||
pointer(errbuf),
|
||||
synced, notifycb_c,
|
||||
inchans > 0 ? notifyhandle(sourcebuf) : C_NULL,
|
||||
outchans > 0 ? notifyhandle(sinkbuf) : C_NULL,
|
||||
notifyhandle(errbuf)
|
||||
)
|
||||
flags = Culong(0)
|
||||
infoptr::Ptr{PortAudio.CallbackInfo{Float32}} = Ptr{PortAudio.CallbackInfo{Float32}}(pointer_from_objref(info))
|
||||
|
||||
testin = zeros(Float32, inchans*nframes)
|
||||
testout = rand(Float32, outchans*nframes)
|
||||
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
|
||||
cb_input = rand(Float32, inchans, nframes) # simulate microphone input
|
||||
cb_output = rand(Float32, outchans, nframes) # this is where the output should go
|
||||
|
||||
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
|
||||
underfill = 3 # should be less than nframes
|
||||
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
|
||||
|
||||
testout = rand(Float32, outchans, nframes) # generate some test data to play
|
||||
write(sinkbuf, testout) # fill the output ringbuffer
|
||||
# test allocation
|
||||
alloc = @allocated ccall(cb, Cint,
|
||||
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{PortAudio.CallbackInfo{Float32}}),
|
||||
inptr, outptr, nframes, C_NULL, flags, infoptr)
|
||||
@test alloc == 0
|
||||
# now test allocation in underrun state
|
||||
alloc = @allocated ccall(cb, Cint,
|
||||
(Ptr{Float32}, Ptr{Float32}, Culong, Ptr{Void}, Culong, Ptr{PortAudio.CallbackInfo{Float32}}),
|
||||
inptr, outptr, nframes, C_NULL, flags, infoptr)
|
||||
@test alloc == 0
|
||||
end
|
||||
@test process() == PortAudio.paContinue
|
||||
if outchans > 0
|
||||
# testout -> sinkbuf -> cb_output
|
||||
@test cb_output == testout
|
||||
end
|
||||
if inchans > 0
|
||||
# cb_input -> sourcebuf
|
||||
@test read(sourcebuf, nframes) == cb_input
|
||||
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
|
||||
|
||||
# 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()
|
||||
i = findfirst(d -> d.maxinchans > 0, devs)
|
||||
indev = i > 0 ? devs[i] : nothing
|
||||
|
@ -92,24 +199,68 @@ end
|
|||
@testset "Reports version" begin
|
||||
io = IOBuffer()
|
||||
PortAudio.versioninfo(io)
|
||||
result = takebuf_string(io)
|
||||
result = split(String(take!((io))), "\n")
|
||||
# make sure this is the same version I tested with
|
||||
@test result ==
|
||||
"""PortAudio V19-devel (built Aug 6 2014 17:54:39)
|
||||
Version Number: 1899
|
||||
"""
|
||||
@test startswith(result[1], "PortAudio V19-devel")
|
||||
@test result[2] == "Version: 1899"
|
||||
@test result[3] == "Shim Source Hash: 4bfafb6888"
|
||||
end
|
||||
|
||||
@testset "PortAudio Callback works for duplex stream" begin
|
||||
test_callback(2, 3)
|
||||
@testset "Basic callback functionality" begin
|
||||
@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
|
||||
|
||||
@testset "Callback works with input-only stream" begin
|
||||
test_callback(2, 0)
|
||||
@testset "Ouput underflow" begin
|
||||
@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
|
||||
|
||||
@testset "Callback works with output-only stream" begin
|
||||
test_callback(0, 2)
|
||||
@testset "Input overflow" begin
|
||||
@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
|
||||
|
||||
@testset "Open Default Device" begin
|
||||
|
@ -139,18 +290,18 @@ end
|
|||
close(stream)
|
||||
end
|
||||
@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)
|
||||
@test size(buf) == (round(Int, 0.001 * samplerate(stream)), nchannels(stream.source))
|
||||
write(stream, buf)
|
||||
io = IOBuffer()
|
||||
show(io, stream)
|
||||
@test takebuf_string(io) == """
|
||||
@test String(take!(io)) == """
|
||||
PortAudio.PortAudioStream{Float32}
|
||||
Samplerate: 48000.0Hz
|
||||
Samplerate: 44100.0Hz
|
||||
Buffer Size: 4096 frames
|
||||
2 channel sink: "Built-in Output"
|
||||
2 channel source: "Built-in Microph\""""
|
||||
2 channel sink: "$default_outdev"
|
||||
2 channel source: "$default_indev\""""
|
||||
close(stream)
|
||||
end
|
||||
@testset "Error on wrong name" begin
|
||||
|
|
Loading…
Reference in a new issue