Compare commits

...

1 commit

Author SHA1 Message Date
Spencer Russell
dc2e216130 this config hangs 2018-08-19 19:14:46 -04:00
3 changed files with 101 additions and 73 deletions

View file

@ -7,7 +7,7 @@ using RingBuffers
using Compat
using Compat: undef, fetch, @compat
using Compat.LinearAlgebra: transpose!
using Compat: stdout
using Compat: stdout, @warn
using Compat.Sys: iswindows
import Base: eltype, show
@ -95,13 +95,15 @@ mutable struct PortAudioStream{T}
Ptr{Pa_StreamParameters}(0) :
Ref(Pa_StreamParameters(outdev.idx, outchans, type_to_fmt[T], 0.0, C_NULL))
this = new(sr, blocksize, C_NULL)
@compat finalizer(close, this)
@compat finalizer(this) do x
close(x)
end
this.sink = PortAudioSink{T}(outdev.name, this, outchans, blocksize*2)
this.source = PortAudioSource{T}(indev.name, this, inchans, blocksize*2)
this.errbuf = RingBuffer{pa_shim_errmsg_t}(1, ERR_BUFSIZE)
if synced && inchans > 0 && outchans > 0
# 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, zeros(T, blocksize*2, outchans))
end
# pass NULL for input/output we're not using
this.bufinfo = pa_shim_info_t(
@ -253,18 +255,17 @@ function handle_errors(stream::PortAudioStream)
err = Vector{pa_shim_errmsg_t}(undef, 1)
while true
nread = read!(stream.errbuf, err)
nread == 1 || break
nread == 1 || break # stream is closed
if err[1] == PA_SHIM_ERRMSG_ERR_OVERFLOW
warn("Error buffer overflowed on stream $(stream.name)")
@warn "Error buffer overflowed on PortAudio stream"
elseif err[1] == PA_SHIM_ERRMSG_OVERFLOW
# warn("Input overflowed from $(name(stream.source))")
elseif err[1] == PA_SHIM_ERRMSG_UNDERFLOW
# warn("Output underflowed to $(name(stream.sink))")
else
error("""
Got unrecognized error code $(err[1]) from audio thread for
stream "$(stream.name)". Please file an issue at
https://github.com/juliaaudio/portaudio.jl/issues""")
throw(ErrorException("""
Got unrecognized error code $(err[1]) from audio thread. Please
file an issue at https://github.com/juliaaudio/portaudio.jl/issues"""))
end
end
end
@ -288,6 +289,7 @@ for (TypeName, Super) in ((:PortAudioSink, :SampleSink),
# it back and forth to julia column-major
chunkbuf = zeros(T, channels, CHUNKSIZE)
ringbuf = RingBuffer{T}(channels, ringbufsize)
breakpoint()
new(name, stream, chunkbuf, ringbuf, channels)
end
end
@ -376,10 +378,11 @@ function set_global_callbacks()
end
function suppress_err(dofunc::Function)
nullfile = @static iswindows() ? "nul" : "/dev/null"
open(nullfile, "w") do io
redirect_stdout(dofunc, io)
end
# nullfile = @static iswindows() ? "nul" : "/dev/null"
# open(nullfile, "w") do io
# redirect_stderr(dofunc, io)
# end
dofunc()
end
function __init__()
@ -389,6 +392,11 @@ function __init__()
suppress_err() do
Pa_Initialize()
end
atexit() do
Pa_Terminate()
end
end
breakpoint(obj=nothing) = ccall(:jl_breakpoint, Cvoid, (Any,), obj)
end # module PortAudio

View file

@ -168,7 +168,7 @@ function Pa_OpenStream(inParams, outParams,
# userdata::T above, and avoid the `pointer_from_objref` below.
# that's not working on 0.6 though, and it shouldn't really
# matter because userdata should be GC-rooted anyways
Ptr{Cvoid}),
Ptr{Void}),
streamPtr, inParams, outParams,
sampleRate, framesPerBuffer, flags, callback,
pointer_from_objref(userdata))

View file

@ -2,7 +2,12 @@
# locally on a machine with a sound card. It's mostly to put the library through
# its paces assuming a human is listening.
include("runtests.jl")
# include("runtests.jl")
using Compat
using PortAudio
using SampledSignals
using Test
using RingBuffers
# these default values are specific to my machines
if Compat.Sys.iswindows()
@ -17,69 +22,84 @@ elseif Compat.Sys.islinux()
end
@testset "Local Tests" begin
@testset "Open Default Device" begin
println("Recording...")
stream = PortAudioStream(2, 0)
buf = read(stream, 5s)
close(stream)
@test size(buf) == (round(Int, 5 * samplerate(stream)), nchannels(stream.source))
println("Playing back recording...")
stream = PortAudioStream(0, 2)
write(stream, buf)
println("flushing...")
flush(stream)
close(stream)
println("Testing pass-through")
stream = PortAudioStream(2, 2)
write(stream, stream, 5s)
flush(stream)
close(stream)
println("done")
end
@testset "Samplerate-converting writing" begin
stream = PortAudioStream(0, 2)
write(stream, SinSource(eltype(stream), samplerate(stream)*0.8, [220, 330]), 3s)
write(stream, SinSource(eltype(stream), samplerate(stream)*1.2, [220, 330]), 3s)
flush(stream)
close(stream)
end
@testset "Open Device by name" begin
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 Compat.occursin("""
PortAudioStream{Float32}
Samplerate: 44100.0Hz
Buffer Size: 4096 frames
2 channel sink: "$default_outdev"
2 channel source: "$default_indev\"""", String(take!(io)))
close(stream)
end
@testset "Error on wrong name" begin
@test_throws ErrorException PortAudioStream("foobarbaz")
end
# @testset "Open Default Device" begin
# println("Recording...")
# stream = PortAudioStream(2, 0)
# buf = read(stream, 5s)
# close(stream)
# @test size(buf) == (round(Int, 5 * samplerate(stream)), nchannels(stream.source))
# println("Playing back recording...")
# stream = PortAudioStream(0, 2)
# write(stream, buf)
# println("flushing...")
# flush(stream)
# close(stream)
# println("Testing pass-through")
# stream = PortAudioStream(2, 2)
# write(stream, stream, 5s)
# flush(stream)
# close(stream)
# println("done")
# end
# @testset "Samplerate-converting writing" begin
# stream = PortAudioStream(0, 2)
# write(stream, SinSource(eltype(stream), samplerate(stream)*0.8, [220, 330]), 3s)
# write(stream, SinSource(eltype(stream), samplerate(stream)*1.2, [220, 330]), 3s)
# flush(stream)
# close(stream)
# end
# @testset "Open Device by name" begin
# 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 Compat.occursin("""
# PortAudioStream{Float32}
# Samplerate: 44100.0Hz
# Buffer Size: 4096 frames
# 2 channel sink: "$default_outdev"
# 2 channel source: "$default_indev\"""", String(take!(io)))
# close(stream)
# end
# @testset "Error on wrong name" begin
# @test_throws ErrorException PortAudioStream("foobarbaz")
# end
# no way to check that the right data is actually getting read or written here,
# but at least it's not crashing.
@testset "Queued Writing" begin
stream = PortAudioStream(0, 2)
buf = SampleBuf(rand(eltype(stream), 48000, nchannels(stream.sink))*0.1, samplerate(stream))
t1 = @async write(stream, buf)
t2 = @async write(stream, buf)
@test fetch(t1) == 48000
@test fetch(t2) == 48000
buf = SampleBuf(rand(eltype(stream), 4800, nchannels(stream.sink))*0.1, samplerate(stream))
io = IOBuffer()
t1 = @async begin
println(io, "t1 - before")
w = write(stream, buf)
println(io, "t1 - after")
w
end
t2 = @async begin
println(io, "t2 - before")
w = write(stream, buf)
println(io, "t2 - after")
w
end
@test fetch(t1) == 4800
@test fetch(t2) == 4800
seekstart(io)
print(read(io, String))
flush(stream)
close(stream)
@show t1.state
@show t2.state
end
@testset "Queued Reading" begin
stream = PortAudioStream(2, 0)
buf = SampleBuf(rand(eltype(stream), 48000, nchannels(stream.source))*0.1, samplerate(stream))
t1 = @async read!(stream, buf)
t2 = @async read!(stream, buf)
@test fetch(t1) == 48000
@test fetch(t2) == 48000
close(stream)
end
# @testset "Queued Reading" begin
# stream = PortAudioStream(2, 0)
# buf = SampleBuf(rand(eltype(stream), 48000, nchannels(stream.source))*0.1, samplerate(stream))
# t1 = @async read!(stream, buf)
# t2 = @async read!(stream, buf)
# @test fetch(t1) == 48000
# @test fetch(t2) == 48000
# close(stream)
# end
end