fix
This commit is contained in:
parent
2bd53c2dd1
commit
175addaf45
2 changed files with 68 additions and 49 deletions
|
@ -74,7 +74,7 @@ function handle_status(error_number; warn_xruns = true)
|
|||
if error_number < 0
|
||||
error_code = PaErrorCode(error_number)
|
||||
if error_code == paOutputUnderflowed || error_code == paInputOverflowed
|
||||
# warn instead of error after an warn_xrun
|
||||
# warn instead of error after an xrun
|
||||
# allow users to disable these warnings
|
||||
if warn_xruns
|
||||
@warn("libportaudio: " * get_error_text(error_number))
|
||||
|
@ -87,7 +87,7 @@ function handle_status(error_number; warn_xruns = true)
|
|||
end
|
||||
|
||||
function initialize()
|
||||
# ALSA will throw extraneous warnings on start up
|
||||
# ALSA will throw extraneous warnings on start-up
|
||||
# send them to debug instead
|
||||
debug_message = @capture_err handle_status(Pa_Initialize())
|
||||
@debug debug_message
|
||||
|
@ -126,10 +126,8 @@ function __init__()
|
|||
end
|
||||
end
|
||||
|
||||
# This size is in frames
|
||||
|
||||
# data is passed to and from portaudio in chunks with this many frames
|
||||
const CHUNKFRAMES = 128
|
||||
const CHUNK_FRAMES = 128
|
||||
|
||||
function versioninfo(io::IO = stdout)
|
||||
println(io, unsafe_string(Pa_GetVersionText()))
|
||||
|
@ -190,7 +188,6 @@ function get_device_info(index::Integer)
|
|||
end
|
||||
|
||||
function get_device_info(device_name::AbstractString)
|
||||
# maintain an error message with avaliable devices while we look to save time
|
||||
for device in devices()
|
||||
potential_match = name(device)
|
||||
if potential_match == device_name
|
||||
|
@ -203,7 +200,8 @@ end
|
|||
function devices()
|
||||
[
|
||||
PortAudioDevice(get_device_info(index), index) for
|
||||
index in 0:(handle_status(Pa_GetDeviceCount()) - 1)
|
||||
# 0 indexing for C
|
||||
index in (1:handle_status(Pa_GetDeviceCount())) .- 1
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -231,7 +229,7 @@ end
|
|||
function Messanger{Sample}(device, channels) where {Sample}
|
||||
Messanger(
|
||||
device,
|
||||
zeros(Sample, channels, CHUNKFRAMES),
|
||||
zeros(Sample, channels, CHUNK_FRAMES),
|
||||
channels,
|
||||
INPUT_CHANNEL_TYPE{Sample}(0),
|
||||
OUTPUT_CHANNEL_TYPE(0),
|
||||
|
@ -374,8 +372,31 @@ function get_default_sample_rates(
|
|||
)
|
||||
end
|
||||
|
||||
# we will spawn a thread to either read or write to port audio
|
||||
# these can be on two separate threads
|
||||
function run_messanger(
|
||||
a_function,
|
||||
pointer_to,
|
||||
port_audio_buffer,
|
||||
inputs,
|
||||
outputs;
|
||||
warn_xruns = true,
|
||||
)
|
||||
while true
|
||||
output = if isopen(inputs)
|
||||
a_function(pointer_to, port_audio_buffer, take!(inputs)...; warn_xruns = warn_xruns)
|
||||
else
|
||||
# no frames can be read/written if the input channel is closed
|
||||
0
|
||||
end
|
||||
# check to see if the output channel has closed too
|
||||
if isopen(outputs)
|
||||
put!(outputs, output)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# we will spawn new threads to read from and write to to port audio
|
||||
# while the reading thread is talking to PortAudio, the writing thread can be setting up
|
||||
function start_messanger(
|
||||
a_function,
|
||||
|
@ -389,27 +410,14 @@ function start_messanger(
|
|||
port_audio_buffer = messanger.port_audio_buffer
|
||||
inputs = messanger.inputs
|
||||
outputs = messanger.outputs
|
||||
@spawn begin
|
||||
while true
|
||||
output = if isopen(inputs)
|
||||
a_function(
|
||||
pointer_to,
|
||||
port_audio_buffer,
|
||||
take!(inputs)...;
|
||||
warn_xruns = warn_xruns,
|
||||
)
|
||||
else
|
||||
# no frames can be read/read if the input channel is closed
|
||||
0
|
||||
end
|
||||
# check to see if the output channel has closed too
|
||||
if isopen(outputs)
|
||||
put!(outputs, output)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
@spawn run_messanger(
|
||||
a_function,
|
||||
pointer_to,
|
||||
port_audio_buffer,
|
||||
inputs,
|
||||
outputs;
|
||||
warn_xruns = warn_xruns,
|
||||
)
|
||||
messanger
|
||||
end
|
||||
|
||||
|
@ -433,8 +441,8 @@ function translate!(
|
|||
end
|
||||
end
|
||||
|
||||
# because we're calling Pa_ReadStream and PA_WriteStream from separate threads,
|
||||
# we put a mutex around libportaudio calls
|
||||
# because we're calling Pa_ReadStream and Pa_WriteStream from separate threads,
|
||||
# we put a lock around these calls
|
||||
const PORT_AUDIO_LOCK = ReentrantLock()
|
||||
|
||||
function real_write!(
|
||||
|
@ -449,7 +457,7 @@ function real_write!(
|
|||
# if we still have frames to write
|
||||
while already < frame_count
|
||||
# take either a whole chunk, or whatever is left if it's smaller
|
||||
chunk_frames = min(frame_count - already, CHUNKFRAMES)
|
||||
chunk_frames = min(frame_count - already, CHUNK_FRAMES)
|
||||
# transpose, then send the data
|
||||
translate!(julia_buffer, port_audio_buffer, chunk_frames, offset, already, false)
|
||||
# TODO: if the stream is closed we just want to return a
|
||||
|
@ -477,7 +485,7 @@ function real_read!(
|
|||
# if we still have frames to write
|
||||
while already < frame_count
|
||||
# take either a whole chunk, or whatever is left if it's smaller
|
||||
chunk_frames = min(frame_count - already, CHUNKFRAMES)
|
||||
chunk_frames = min(frame_count - already, CHUNK_FRAMES)
|
||||
# receive the data, then transpose
|
||||
# TODO: if the stream is closed we just want to return a
|
||||
# shorter-than-requested frame count instead of throwing an error
|
||||
|
@ -535,14 +543,14 @@ function PortAudioStream(
|
|||
output_channels,
|
||||
output_device,
|
||||
),
|
||||
frames_per_buffer = 0,
|
||||
warn_xruns = true,
|
||||
# these defaults are currently undocumented
|
||||
frames_per_buffer = 0,
|
||||
flags = paNoFlag,
|
||||
call_back = nothing,
|
||||
user_data = nothing,
|
||||
input_info = nothing,
|
||||
output_info = nothing,
|
||||
warn_xruns = true,
|
||||
)
|
||||
input_channels_filled, output_channels_filled =
|
||||
fill_both_channels(input_channels, input_device, output_channels, output_device)
|
||||
|
@ -648,6 +656,7 @@ function close(stream::PortAudioStream)
|
|||
close(stream.sink_messanger)
|
||||
close(stream.source_messanger)
|
||||
pointer_to = stream.pointer_to
|
||||
# only stop if it's not arlready stopped
|
||||
if !Bool(handle_status(Pa_IsStreamStopped(pointer_to)))
|
||||
handle_status(Pa_StopStream(pointer_to))
|
||||
end
|
||||
|
|
|
@ -51,7 +51,7 @@ using PortAudio.LibPortAudio:
|
|||
PaStreamInfo,
|
||||
PaStreamParameters,
|
||||
PaVersionInfo
|
||||
using SampledSignals: nchannels, s, SampleBuf, samplerate, SinSourc
|
||||
using SampledSignals: nchannels, s, SampleBuf, samplerate, SinSource
|
||||
using Test: @test, @test_logs, @test_nowarn, @testset, @test_throws
|
||||
|
||||
@testset "Tests without sound" begin
|
||||
|
@ -72,7 +72,8 @@ using Test: @test, @test_logs, @test_nowarn, @testset, @test_throws
|
|||
@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
|
||||
@test safe_load(Pa_GetVersionInfo(), ErrorException("no info")) isa
|
||||
PaVersionInfo
|
||||
end
|
||||
@test safe_load(Pa_GetLastHostErrorInfo(), ErrorException("no info")) isa
|
||||
PaHostErrorInfo
|
||||
|
@ -83,10 +84,13 @@ using Test: @test, @test_logs, @test_nowarn, @testset, @test_throws
|
|||
end
|
||||
|
||||
@testset "Errors without sound" begin
|
||||
@test_throws ArgumentError("Default input and output sample rates disagree") combine_default_sample_rates(0, 1)
|
||||
@test_throws ArgumentError("Default input and output sample rates disagree") combine_default_sample_rates(
|
||||
0,
|
||||
1,
|
||||
)
|
||||
wrong = "foobarbaz"
|
||||
@test sprint(showerror, PortAudioException(paNotInitialized)) ==
|
||||
"PortAudioException: PortAudio not initialized"
|
||||
@test sprint(showerror, PortAudioException(paNotInitialized)) ==
|
||||
"PortAudioException: PortAudio not initialized"
|
||||
@test_throws KeyError(wrong) get_device_info(wrong)
|
||||
@test_throws BoundsError(Pa_GetDeviceInfo, -1) get_device_info(-1)
|
||||
@test_throws ArgumentError("Could not find ALSA config") seek_alsa_conf([])
|
||||
|
@ -94,7 +98,7 @@ using Test: @test, @test_logs, @test_nowarn, @testset, @test_throws
|
|||
PaError(paOutputUnderflowed),
|
||||
)
|
||||
@test_throws PortAudioException(paNotInitialized) handle_status(
|
||||
PaError(paNotInitialized)
|
||||
PaError(paNotInitialized),
|
||||
)
|
||||
Pa_Sleep(1)
|
||||
@test Pa_GetSampleSize(paFloat32) == 4
|
||||
|
@ -106,7 +110,7 @@ if !isempty(devices())
|
|||
terminate()
|
||||
initialize()
|
||||
|
||||
# these default values are specific to my machines
|
||||
# these default values are specific to local machines
|
||||
input_index = get_default_input_device()
|
||||
default_input_device = PortAudioDevice(get_device_info(input_index), input_index).name
|
||||
output_index = get_default_output_device()
|
||||
|
@ -160,7 +164,7 @@ if !isempty(devices())
|
|||
end
|
||||
end
|
||||
@testset "Open Device by name" begin
|
||||
PortAudioStream(default_input_device, default_output_device) do stream
|
||||
PortAudioStream(default_input_device, default_output_device) do stream
|
||||
end
|
||||
end
|
||||
# no way to check that the right data is actually getting read or written here,
|
||||
|
@ -198,12 +202,18 @@ if !isempty(devices())
|
|||
end
|
||||
end
|
||||
@testset "Errors with sound" begin
|
||||
@test_throws DomainError(typemax(Int), "max channels exceeded") PortAudioStream(typemax(Int), 0)
|
||||
@test_throws ArgumentError("Input or output must have at least 1 channel") PortAudioStream(0, 0)
|
||||
@test_throws DomainError(typemax(Int), "max channels exceeded") PortAudioStream(
|
||||
typemax(Int),
|
||||
0,
|
||||
)
|
||||
@test_throws ArgumentError("Input or output must have at least 1 channel") PortAudioStream(
|
||||
0,
|
||||
0,
|
||||
)
|
||||
end
|
||||
@testset "libportaudio with sound" begin
|
||||
@test PaErrorCode(Pa_HostApiTypeIdToHostApiIndex(paInDevelopment)) ==
|
||||
paHostApiNotFound
|
||||
paHostApiNotFound
|
||||
@test Pa_HostApiDeviceIndexToDeviceIndex(paInDevelopment, 0) == 0
|
||||
stream = PortAudioStream(2, 2)
|
||||
pointer_to = stream.pointer_to
|
||||
|
@ -222,4 +232,4 @@ if !isempty(devices())
|
|||
end
|
||||
end
|
||||
|
||||
# sample rates disagree
|
||||
# sample rates disagree
|
||||
|
|
Loading…
Add table
Reference in a new issue