PortAudio.jl/test/runtests.jl
Brandon Taylor d6c3595f03 Use Clang wrappers; reduce thread spawning; separate out SampledSignals
fix

fix

use CLANG wrappers

cleanup (again)

more coverage

fix tests

fix?

distinguish error numbers from codes

reduce thread spawning

cleanup

fix?

fix?

coverage

coverage

fix

fix

more cleanup and comments

separate out SampledSignals part

almost there

fix

comments

fix

Add gen README

Update test/runtests.jl

Co-authored-by: Robert Luke <748691+rob-luke@users.noreply.github.com>
performance improvements

fix

more comments

separate messanger from buffer

fix source/sink mix-up

adjust_channels, test device names

slight cleanup

update docs

add links to docs to readme
2021-07-25 13:11:55 -04:00

252 lines
9.3 KiB
Julia
Executable file

#!/usr/bin/env julia
using Base.Sys: iswindows
using Documenter: doctest
using PortAudio:
combine_default_sample_rates,
devices,
get_default_input_index,
get_default_output_index,
get_device,
get_input_type,
get_output_type,
handle_status,
initialize,
PortAudioException,
PortAudio,
PortAudioDevice,
PortAudioStream,
safe_load,
seek_alsa_conf,
terminate,
name
using PortAudio.LibPortAudio:
Pa_AbortStream,
PaError,
PaErrorCode,
paFloat32,
Pa_GetDefaultHostApi,
Pa_GetDeviceInfo,
Pa_GetHostApiCount,
Pa_GetLastHostErrorInfo,
Pa_GetSampleSize,
Pa_GetStreamCpuLoad,
Pa_GetStreamInfo,
Pa_GetStreamReadAvailable,
Pa_GetStreamTime,
Pa_GetStreamWriteAvailable,
Pa_GetVersionInfo,
Pa_HostApiDeviceIndexToDeviceIndex,
paHostApiNotFound,
Pa_HostApiTypeIdToHostApiIndex,
PaHostErrorInfo,
paInDevelopment,
paInvalidDevice,
Pa_IsFormatSupported,
Pa_IsStreamActive,
paNoError,
paNoFlag,
paNotInitialized,
Pa_OpenDefaultStream,
paOutputUnderflowed,
Pa_SetStreamFinishedCallback,
Pa_Sleep,
Pa_StopStream,
PaStream,
PaStreamInfo,
PaStreamParameters,
PaVersionInfo
using SampledSignals: nchannels, s, SampleBuf, samplerate, SinSource
using Test: @test, @test_logs, @test_nowarn, @testset, @test_throws
@testset "Tests without sound" begin
@testset "Reports version" begin
io = IOBuffer()
PortAudio.versioninfo(io)
result = split(String(take!((io))), "\n")
# make sure this is the same version I tested with
@test startswith(result[1], "PortAudio V19")
end
@testset "Can list devices without crashing" begin
display(devices())
println()
end
@testset "libortaudio without sound" begin
@test handle_status(Pa_GetHostApiCount()) >= 0
@test handle_status(Pa_GetDefaultHostApi()) >= 0
# version info not available on windows?
if !Sys.iswindows()
@test safe_load(Pa_GetVersionInfo(), ErrorException("no info")) isa
PaVersionInfo
end
@test safe_load(Pa_GetLastHostErrorInfo(), ErrorException("no info")) isa
PaHostErrorInfo
@test PaErrorCode(Pa_IsFormatSupported(C_NULL, C_NULL, 0.0)) == paInvalidDevice
@test PaErrorCode(
Pa_OpenDefaultStream(Ref(C_NULL), 0, 0, paFloat32, 0.0, 0, C_NULL, C_NULL),
) == paInvalidDevice
end
@testset "Errors without sound" begin
@test sprint(showerror, PortAudioException(paNotInitialized)) ==
"PortAudioException: PortAudio not initialized"
@test_throws KeyError("foobarbaz") get_device("foobarbaz")
@test_throws KeyError(-1) get_device(-1)
@test_throws ArgumentError("Could not find alsa.conf in ()") seek_alsa_conf(())
@test_logs (:warn, "libportaudio: Output underflowed") handle_status(
PaError(paOutputUnderflowed),
)
@test_throws PortAudioException(paNotInitialized) handle_status(
PaError(paNotInitialized),
)
Pa_Sleep(1)
@test Pa_GetSampleSize(paFloat32) == 4
end
# make sure we can terminate, then reinitialize
terminate()
initialize()
end
if !isempty(devices())
@testset "Tests with sound" begin
# these default values are specific to local machines
input_name = get_device(get_default_input_index()).name
output_name = get_device(get_default_output_index()).name
@testset "Interactive tests" begin
println("Recording...")
stream = PortAudioStream(input_name, output_name, 2, 0; adjust_channels = true)
buffer = read(stream, 5s)
@test size(buffer) ==
(round(Int, 5 * samplerate(stream)), nchannels(stream.source))
close(stream)
sleep(1)
println("Playing back recording...")
PortAudioStream(input_name, output_name, 0, 2; adjust_channels = true) do stream
write(stream, buffer)
end
sleep(1)
println("Testing pass-through")
stream = PortAudioStream(input_name, output_name, 2, 2; adjust_channels = true)
sink = stream.sink
source = stream.source
@test sprint(show, stream) == """
PortAudioStream{Float32}
Samplerate: 44100.0Hz
2 channel sink: $(repr(input_name))
2 channel source: $(repr(output_name))"""
@test sprint(show, source) == "2 channel source: $(repr(output_name))"
@test sprint(show, sink) == "2 channel sink: $(repr(input_name))"
write(stream, stream, 5s)
@test PaErrorCode(handle_status(Pa_StopStream(stream.pointer_to))) == paNoError
@test isopen(stream)
close(stream)
sleep(1)
@test !isopen(stream)
@test !isopen(sink)
@test !isopen(source)
println("done")
end
@testset "Samplerate-converting writing" begin
PortAudioStream(input_name, output_name, 0, 2; adjust_channels = true) do stream
write(
stream,
SinSource(eltype(stream), samplerate(stream) * 0.8, [220, 330]),
3s,
)
println("expected blip")
write(
stream,
SinSource(eltype(stream), samplerate(stream) * 1.2, [220, 330]),
3s,
)
end
end
sleep(1)
# 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
PortAudioStream(input_name, output_name, 0, 2; adjust_channels = true) do stream
buffer = SampleBuf(
rand(eltype(stream), 48000, nchannels(stream.sink)) * 0.1,
samplerate(stream),
)
frame_count_1 = @async write(stream, buffer)
frame_count_2 = @async write(stream, buffer)
@test fetch(frame_count_1) == 48000
println("expected blip")
@test fetch(frame_count_2) == 48000
end
sleep(1)
end
@testset "Queued Reading" begin
PortAudioStream(input_name, output_name, 2, 0; adjust_channels = true) do stream
buffer = SampleBuf(
rand(eltype(stream), 48000, nchannels(stream.source)) * 0.1,
samplerate(stream),
)
frame_count_1 = @async read!(stream, buffer)
frame_count_2 = @async read!(stream, buffer)
@test fetch(frame_count_1) == 48000
@test fetch(frame_count_2) == 48000
end
sleep(1)
end
@testset "Constructors" begin
PortAudioStream(2, maximum; adjust_channels = true) do stream
@test isopen(stream)
end
PortAudioStream(output_name; adjust_channels = true) do stream
@test isopen(stream)
end
PortAudioStream(input_name, output_name; adjust_channels = true) do stream
@test isopen(stream)
end
end
@testset "Errors with sound" begin
big = typemax(Int)
@test_throws DomainError(
typemax(Int),
"$big exceeds maximum input channels for $output_name",
) PortAudioStream(input_name, output_name, big, 0)
@test_throws ArgumentError("Input or output must have at least 1 channel") PortAudioStream(
input_name,
output_name,
0,
0;
adjust_channels = true,
)
@test_throws ArgumentError("""
Default sample rate 0 for input $output_name disagrees with
default sample rate 1 for output $input_name.
Please specify a sample rate.
""") combine_default_sample_rates(
get_device(input_name),
0,
get_device(output_name),
1,
)
end
@testset "libportaudio with sound" begin
@test PaErrorCode(Pa_HostApiTypeIdToHostApiIndex(paInDevelopment)) ==
paHostApiNotFound
@test Pa_HostApiDeviceIndexToDeviceIndex(paInDevelopment, 0) == 0
stream = PortAudioStream(input_name, output_name, 2, 2; adjust_channels = true)
pointer_to = stream.pointer_to
@test handle_status(Pa_GetStreamReadAvailable(pointer_to)) >= 0
@test handle_status(Pa_GetStreamWriteAvailable(pointer_to)) >= 0
@test Bool(handle_status(Pa_IsStreamActive(pointer_to)))
@test safe_load(Pa_GetStreamInfo(pointer_to), ErrorException("no info")) isa
PaStreamInfo
@test Pa_GetStreamTime(pointer_to) >= 0
@test Pa_GetStreamCpuLoad(pointer_to) >= 0
@test PaErrorCode(handle_status(Pa_AbortStream(pointer_to))) == paNoError
@test PaErrorCode(
handle_status(Pa_SetStreamFinishedCallback(pointer_to, C_NULL)),
) == paNoError
end
end
doctest(PortAudio)
end