Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
|
dc2e216130 |
3 changed files with 101 additions and 73 deletions
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue