now will do partial ringbuf read/writes in partial underflow conditions. crashes on 0.5 --inline-no

This commit is contained in:
Spencer Russell 2016-08-01 13:20:53 -04:00
parent fd425b3ace
commit 0d64e4bd0c
2 changed files with 62 additions and 26 deletions

View file

@ -276,19 +276,31 @@ function portaudio_callback{T}(inptr::Ptr{T}, outptr::Ptr{T},
info = unsafe_load(userdata)
insamples = nframes * info.inchannels
outsamples = nframes * info.outchannels
if nreadable(info.outbuf) < outsamples ||
nwritable(info.inbuf) < insamples
# xrun, copy zeros to outbuffer
# TODO: send a notification to an error msg ringbuf
# TODO (maybe): we could do a partial write if there's anything in the
# ringbuf, and minimize the dropout
memset(outptr, 0, sizeof(T)*outsamples)
return paContinue
bufsamples = if insamples == UInt(0) && outsamples > UInt(0)
# playback-only
nreadable(info.outbuf)
elseif insamples > UInt(0) && outsamples == UInt(0)
# record-only
nwritable(info.inbuf)
elseif insamples > UInt(0) && outsamples > UInt(0)
# duplex
min(nreadable(info.outbuf), nwritable(info.inbuf))
else
UInt(0)
end
read!(info.outbuf, outptr, outsamples)
write(info.inbuf, inptr, insamples)
toread = min(bufsamples, outsamples)
towrite = min(bufsamples, insamples)
read!(info.outbuf, outptr, toread)
write(info.inbuf, inptr, towrite)
if toread < outsamples
# xrun, copy zeros to outbuffer
# TODO: send a notification to an error msg ringbuf
memset(outptr+sizeof(T)*toread, 0, sizeof(T)*(outsamples-toread))
return paContinue
end
paContinue
end

View file

@ -32,43 +32,67 @@ using RingBuffers
end
@testset "PortAudio Callback works and doesn't allocate" begin
cb = PortAudio.pa_callbacks[Float32]
inbuf = rand(Float32, 16) # simulate microphone input
sourcebuf = LockFreeRingBuffer(Float32, 64) # the microphone input should end up here
testin = zeros(Float32, 16)
outbuf = zeros(Float32, 24) # this is where the output should go
sinkbuf = LockFreeRingBuffer(Float32, 64) # the callback should copy this to outbuf
testout = rand(Float32, 24)
write(sinkbuf, testout) # fill the output ringbuffer
# 2 input channels, 3 output channels
info = PortAudio.CallbackInfo(2, sourcebuf, 3, sinkbuf)
# handle any conversions here so they don't mess with the allocation
inptr = pointer(inbuf)
outptr = pointer(outbuf)
# 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))
nframes = Culong(8)
flags = Culong(0)
infoptr = Ptr{PortAudio.CallbackInfo{Float32}}(pointer_from_objref(info))
infoptr::Ptr{PortAudio.CallbackInfo{Float32}} = Ptr{PortAudio.CallbackInfo{Float32}}(pointer_from_objref(info))
ret = PortAudio.portaudio_callback(inptr, outptr, nframes, C_NULL, flags, infoptr)
@test isa(ret, Cint)
@test ret == PortAudio.paContinue
testin = zeros(Float32, 16)
testout = rand(Float32, 24)
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
# call again (underrun)
ret = PortAudio.portaudio_callback(inptr, outptr, nframes, C_NULL, flags, infoptr)
@test isa(ret, Cint)
@test ret == PortAudio.paContinue
testout = rand(Float32, 10)
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:10] == testout
@test outbuf[11:24] == zeros(Float32, 14)
@test nreadable(sourcebuf) == 10
@test read!(sourcebuf, testin) == 10
@test testin[1:10] == inbuf[1:10]
# 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, 24)
@test nreadable(sourcebuf) == 0
write(sinkbuf, testout) # fill the output ringbuffer
# test allocation
alloc = @allocated PortAudio.portaudio_callback(inptr, outptr, nframes, C_NULL, flags, infoptr)
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 PortAudio.portaudio_callback(inptr, outptr, nframes, C_NULL, flags, infoptr)
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