cleanup (again)

This commit is contained in:
Brandon Taylor 2021-06-17 14:28:55 -04:00
parent 4f62de7fda
commit 138551ec80
2 changed files with 85 additions and 87 deletions

View file

@ -1,48 +1,45 @@
module PortAudio
using alsa_plugins_jll: alsa_plugins_jll
import Base: close, eltype, getproperty, isopen, read, read!, show, write
using libportaudio_jll: libportaudio
using SampledSignals
using Suppressor: @capture_err
import Base: eltype, getproperty, show
import Base: close, isopen
import Base: read, read!, write
using LinearAlgebra: transpose!
import SampledSignals: nchannels, samplerate, unsafe_read!, unsafe_write
using SampledSignals: SampleSink, SampleSource
using Suppressor: @capture_err
export PortAudioStream
include("libportaudio.jl")
using .LibPortAudio:
PaSampleFormat,
Pa_Initialize,
Pa_Terminate,
Pa_GetVersion,
PaDeviceIndex,
PaDeviceInfo,
Pa_GetVersionText,
PaHostApiTypeId,
Pa_GetHostApiInfo,
PaStream,
Pa_GetDeviceCount,
Pa_GetDeviceInfo,
Pa_GetDefaultInputDevice,
Pa_GetDefaultOutputDevice,
Pa_OpenStream,
Pa_StartStream,
Pa_StopStream,
Pa_CloseStream,
Pa_GetStreamReadAvailable,
Pa_GetStreamWriteAvailable,
Pa_ReadStream,
Pa_WriteStream,
Pa_GetErrorText,
paOutputUnderflowed,
using .LibPortAudio:
Pa_CloseStream,
PaDeviceIndex,
PaDeviceInfo,
PaErrorCode,
Pa_GetDefaultInputDevice,
Pa_GetDefaultOutputDevice,
Pa_GetDeviceCount,
Pa_GetDeviceInfo,
Pa_GetErrorText,
Pa_GetHostApiInfo,
Pa_GetStreamReadAvailable,
Pa_GetStreamWriteAvailable,
Pa_GetVersionText,
Pa_GetVersion,
PaHostApiTypeId,
Pa_Initialize,
paInputOverflowed,
paNoFlag,
Pa_OpenStream,
paOutputUnderflowed,
Pa_ReadStream,
PaSampleFormat,
Pa_StartStream,
Pa_StopStream,
PaStream,
PaStreamParameters,
PaErrorCode
Pa_Terminate,
Pa_WriteStream
function safe_load(result, an_error)
if result == C_NULL
@ -110,6 +107,14 @@ macro stderr_as_debug(expression)
end
end
function initialize()
@stderr_as_debug handle_status(@locked Pa_Initialize())
end
function terminate()
handle_status(@locked Pa_Terminate())
end
# This size is in frames
# data is passed to and from portaudio in chunks with this many frames, because
@ -139,10 +144,12 @@ end
function PortAudioDevice(info::PaDeviceInfo, idx)
PortAudioDevice(
unsafe_string(info.name),
unsafe_string(safe_load(
(@locked Pa_GetHostApiInfo(info.hostApi)),
BoundsError(Pa_GetHostApiInfo, idx),
).name),
unsafe_string(
safe_load(
(@locked Pa_GetHostApiInfo(info.hostApi)),
BoundsError(Pa_GetHostApiInfo, idx),
).name,
),
info.defaultSampleRate,
idx,
Bounds(
@ -160,15 +167,23 @@ end
name(device::PortAudioDevice) = device.name
function get_default_input_device()
handle_status(@locked Pa_GetDefaultInputDevice())
end
function get_default_output_device()
handle_status(@locked Pa_GetDefaultOutputDevice())
end
function get_device_info(i)
safe_load(
(@locked Pa_GetDeviceInfo(i)),
BoundsError(Pa_GetDeviceInfo, i),
)
safe_load((@locked Pa_GetDeviceInfo(i)), BoundsError(Pa_GetDeviceInfo, i))
end
function devices()
[PortAudioDevice(get_device_info(i), i) for i in 0:(handle_status(@locked Pa_GetDeviceCount()) - 1)]
[
PortAudioDevice(get_device_info(i), i) for
i in 0:(handle_status(@locked Pa_GetDeviceCount()) - 1)
]
end
struct Buffer{T}
@ -382,8 +397,8 @@ end
# use the default input and output devices
function PortAudioStream(inchans = 2, outchans = 2; kwargs...)
inidx = handle_status(@locked Pa_GetDefaultInputDevice())
outidx = handle_status(@locked Pa_GetDefaultOutputDevice())
inidx = get_default_input_device()
outidx = get_default_output_device()
PortAudioStream(
PortAudioDevice(get_device_info(inidx), inidx),
PortAudioDevice(get_device_info(outidx), outidx),
@ -416,7 +431,7 @@ end
isopen(stream::PortAudioStream) = stream.pointer_ref[] != C_NULL
SampledSignals.samplerate(stream::PortAudioStream) = stream.samplerate
samplerate(stream::PortAudioStream) = stream.samplerate
eltype(::Type{PortAudioStream{T}}) where {T} = T
read(stream::PortAudioStream, args...) = read(stream.source, args...)
@ -470,12 +485,12 @@ function Buffer{T}(device, channels) where {T}
Buffer(device, chunkbuf, channels)
end
SampledSignals.nchannels(buffer::Buffer) = buffer.nchannels
nchannels(buffer::Buffer) = buffer.nchannels
name(buffer::Buffer) = name(buffer.device)
SampledSignals.nchannels(s::PortAudioSource) = nchannels(s.stream.source_buffer)
SampledSignals.nchannels(s::PortAudioSink) = nchannels(s.stream.sink_buffer)
SampledSignals.samplerate(s::Union{PortAudioSink, PortAudioSource}) = samplerate(s.stream)
nchannels(s::PortAudioSource) = nchannels(s.stream.source_buffer)
nchannels(s::PortAudioSink) = nchannels(s.stream.sink_buffer)
samplerate(s::Union{PortAudioSink, PortAudioSource}) = samplerate(s.stream)
eltype(::Type{<:Union{PortAudioSink{T}, PortAudioSource{T}}}) where {T} = T
function close(::Union{PortAudioSink, PortAudioSource})
throw(ErrorException("""
@ -519,22 +534,13 @@ end
function write_stream(stream::Ptr{PaStream}, buf::Array, frames::Integer; warn_xruns = true)
handle_status(
disable_sigint() do
@tcall @locked Pa_WriteStream(
stream,
buf,
frames,
)
@tcall @locked Pa_WriteStream(stream, buf, frames)
end,
warn_xruns = warn_xruns,
)
end
function SampledSignals.unsafe_write(
sink::PortAudioSink,
buf::Array,
frameoffset,
framecount,
)
function unsafe_write(sink::PortAudioSink, buf::Array, frameoffset, framecount)
stream = sink.stream
pointer = stream.pointer_ref[]
chunkbuf = stream.sink_buffer.chunkbuf
@ -566,22 +572,13 @@ function read_stream(stream::Ptr{PaStream}, buf::Array, frames::Integer; warn_xr
# don't use ctrl-C.
handle_status(
disable_sigint() do
@tcall @locked Pa_ReadStream(
stream,
buf,
frames
)
@tcall @locked Pa_ReadStream(stream, buf, frames)
end,
warn_xruns = warn_xruns,
)
end
function SampledSignals.unsafe_read!(
source::PortAudioSource,
buf::Array,
frameoffset,
framecount,
)
function unsafe_read!(source::PortAudioSource, buf::Array, frameoffset, framecount)
stream = source.stream
pointer = stream.pointer_ref[]
chunkbuf = stream.source_buffer.chunkbuf
@ -682,10 +679,10 @@ function __init__()
# junk to STDOUT on initialization, so we swallow it.
# TODO: actually check the junk to make sure there's nothing in there we
# don't expect
@stderr_as_debug handle_status(@locked Pa_Initialize())
@stderr_as_debug handle_status(initialize())
atexit() do
handle_status(@locked Pa_Terminate())
handle_status(@locked terminate())
end
end

View file

@ -1,24 +1,25 @@
#!/usr/bin/env julia
using Logging: Debug
using PortAudio
using PortAudio:
combine_default_sample_rates,
devices,
get_default_input_device,
get_default_output_device,
get_device_info,
handle_status,
Pa_GetDefaultInputDevice,
Pa_GetDefaultOutputDevice,
Pa_Initialize,
initialize,
paOutputUnderflowed,
Pa_Terminate,
PortAudio,
PortAudioDevice,
PortAudioStream,
recover_xrun,
seek_alsa_conf,
@stderr_as_debug,
@locked
terminate
using PortAudio.LibPortAudio: paNotInitialized
using SampledSignals
using Test
using SampledSignals: nchannels, s, SampleBuf, samplerate, SinSource
using Test: @test, @test_logs, @test_nowarn, @testset, @test_throws
@testset "Debug messages" begin
@test_logs (:debug, "hi") min_level = Debug @test_nowarn @stderr_as_debug begin
@ -37,7 +38,7 @@ end
end
@testset "Can list devices without crashing" begin
PortAudio.devices()
devices()
end
@testset "Null errors" begin
@ -45,15 +46,15 @@ end
end
end
if !isempty(PortAudio.devices())
if !isempty(devices())
# make sure we can terminate, then reinitialize
handle_status(@locked Pa_Terminate())
@stderr_as_debug handle_status(@locked Pa_Initialize())
terminate()
initialize()
# these default values are specific to my machines
inidx = handle_status(@locked Pa_GetDefaultInputDevice())
inidx = get_default_input_device()
default_indev = PortAudioDevice(get_device_info(inidx), inidx).name
outidx = handle_status(@locked Pa_GetDefaultOutputDevice())
outidx = get_default_output_device()
default_outdev = PortAudioDevice(get_device_info(outidx), outidx).name
@testset "Local Tests" begin