diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index f1d472d..f21b7bc 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: version: - - '1.3' + - '1.6' - '1' - 'nightly' os: diff --git a/Project.toml b/Project.toml index cf1b455..c82362b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PortAudio" uuid = "80ea8bcb-4634-5cb3-8ee8-a132660d1d2d" repo = "https://github.com/JuliaAudio/PortAudio.jl.git" -version = "1.2.0" +version = "1.3.0" [deps] alsa_plugins_jll = "5ac2f6bb-493e-5871-9171-112d4c21a6e7" @@ -11,7 +11,7 @@ SampledSignals = "bd7594eb-a658-542f-9e75-4c4d8908c167" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" [compat] -julia = "1.3" +julia = "1.6" alsa_plugins_jll = "1.2.2" libportaudio_jll = "19.6.0" SampledSignals = "2.1.1" diff --git a/src/PortAudio.jl b/src/PortAudio.jl index 4ee7630..7e9e93f 100644 --- a/src/PortAudio.jl +++ b/src/PortAudio.jl @@ -230,19 +230,25 @@ function devices() end # we can handle reading and writing from buffers in a similar way -function read_or_write(a_function, buffer, use_frames = buffer.frames_per_buffer) +function read_or_write(a_function, buffer, use_frames = buffer.frames_per_buffer; acquire_lock = true) + pointer_to = buffer.pointer_to + data = buffer.data handle_status( - # because we're calling Pa_ReadStream and Pa_WriteStream from separate threads, - # we put a lock around these calls - lock( - let a_function = a_function, - pointer_to = buffer.pointer_to, - data = buffer.data, - use_frames = use_frames - () -> a_function(pointer_to, data, use_frames) - end, - buffer.stream_lock, - ), + if acquire_lock + # because we're calling Pa_ReadStream and Pa_WriteStream from separate threads, + # we put a lock around these calls + lock( + let a_function = a_function, + pointer_to = pointer_to, + data = data, + use_frames = use_frames + () -> a_function(pointer_to, data, use_frames) + end, + buffer.stream_lock, + ) + else + a_function(pointer_to, data, use_frames) + end; warn_xruns = buffer.warn_xruns, ) end @@ -407,21 +413,25 @@ eltype(::Type{Buffer{Sample}}) where {Sample} = Sample nchannels(buffer::Buffer) = buffer.number_of_channels """ - PortAudio.write_buffer(buffer, use_frames = buffer.frames_per_buffer) + PortAudio.write_buffer(buffer, use_frames = buffer.frames_per_buffer; acquire_lock = true) Write a number of frames (`use_frames`) from a [`PortAudio.Buffer`](@ref) to PortAudio. + +Set `acquire_lock = false` to skip acquiring the lock. """ -function write_buffer(buffer::Buffer, use_frames = buffer.frames_per_buffer) - read_or_write(Pa_WriteStream, buffer, use_frames) +function write_buffer(buffer::Buffer, use_frames = buffer.frames_per_buffer; acquire_lock = true) + read_or_write(Pa_WriteStream, buffer, use_frames; acquire_lock = acquire_lock) end """ - PortAudio.read_buffer!(buffer::Buffer, use_frames = buffer.frames_per_buffer) + PortAudio.read_buffer!(buffer::Buffer, use_frames = buffer.frames_per_buffer; acquire_lock = true) Read a number of frames (`use_frames`) from PortAudio to a [`PortAudio.Buffer`](@ref). + +Set `acquire_lock = false` to skip acquiring the acquire_lock. """ -function read_buffer!(buffer, use_frames = buffer.frames_per_buffer) - read_or_write(Pa_ReadStream, buffer, use_frames) +function read_buffer!(buffer, use_frames = buffer.frames_per_buffer; acquire_lock = true) + read_or_write(Pa_ReadStream, buffer, use_frames; acquire_lock = acquire_lock) end """ @@ -594,10 +604,9 @@ function combine_default_sample_rates( ) if input_sample_rate != output_sample_rate throw( - ArgumentError( - """ -Default sample rate $input_sample_rate for input $(name(input_device)) disagrees with -default sample rate $output_sample_rate for output $(name(output_device)). + ArgumentError(""" +Default sample rate $input_sample_rate for input \"$(name(input_device))\" disagrees with +default sample rate $output_sample_rate for output \"$(name(output_device))\". Please specify a sample rate. """, ), @@ -907,6 +916,7 @@ end isopen(stream::PortAudioStream) = isopen(stream.pointer_to) samplerate(stream::PortAudioStream) = stream.sample_rate + function eltype( ::Type{<:PortAudioStream{<:Messenger{Sample}, <:Messenger{Sample}}}, ) where {Sample} @@ -927,7 +937,7 @@ function show(io::IO, stream::PortAudioStream) print(io, "PortAudioStream{") print(io, eltype(stream)) println(io, "}") - print(io, " Samplerate: ", samplerate(stream), "Hz") + print(io, " Samplerate: ", round(Int, samplerate(stream)), "Hz") # show source or sink if there's any channels sink = stream.sink if has_channels(sink) @@ -1043,4 +1053,6 @@ function unsafe_read!( exchange(source.stream.source_messenger, as_matrix(julia_buffer), already, frame_count) end +include("precompile.jl") + end # module PortAudio diff --git a/src/precompile.jl b/src/precompile.jl new file mode 100644 index 0000000..7dd9ab3 --- /dev/null +++ b/src/precompile.jl @@ -0,0 +1,29 @@ +# precompile some important functions +const DEFAULT_SINK_MESSENGER_TYPE = Messenger{Float32, SampledSignalsWriter, Tuple{Matrix{Float32}, Int64, Int64}, Int64} +const DEFAULT_SOURCE_MESSENGER_TYPE = Messenger{Float32, SampledSignalsReader, Tuple{Matrix{Float32}, Int64, Int64}, Int64} +const DEFAULT_STREAM_TYPE = PortAudioStream{DEFAULT_SINK_MESSENGER_TYPE, DEFAULT_SOURCE_MESSENGER_TYPE} +const DEFAULT_SINK_TYPE = PortAudioSink{DEFAULT_SINK_MESSENGER_TYPE, DEFAULT_SOURCE_MESSENGER_TYPE} +const DEFAULT_SOURCE_TYPE = PortAudioSource{DEFAULT_SINK_MESSENGER_TYPE, DEFAULT_SOURCE_MESSENGER_TYPE} + +precompile(close, (DEFAULT_STREAM_TYPE,)) +precompile(devices, ()) +precompile(__init__, ()) +precompile(isopen, (DEFAULT_STREAM_TYPE,)) +precompile(nchannels, (DEFAULT_SINK_TYPE,)) +precompile(nchannels, (DEFAULT_SOURCE_TYPE,)) +precompile(PortAudioStream, (Int, Int)) +precompile(PortAudioStream, (String, Int, Int)) +precompile(PortAudioStream, (String, String, Int, Int)) +precompile(samplerate, (DEFAULT_STREAM_TYPE,)) +precompile(send, (DEFAULT_SINK_MESSENGER_TYPE,)) +precompile(send, (DEFAULT_SOURCE_MESSENGER_TYPE,)) +precompile(unsafe_read!, (DEFAULT_SOURCE_TYPE, Vector{Float32}, Int, Int)) +precompile(unsafe_read!, (DEFAULT_SOURCE_TYPE, Matrix{Float32}, Int, Int)) +precompile(unsafe_write, (DEFAULT_SINK_TYPE, Vector{Float32}, Int, Int)) +precompile(unsafe_write, (DEFAULT_SINK_TYPE, Matrix{Float32}, Int, Int)) + + + + + + diff --git a/test/runtests.jl b/test/runtests.jl index ee2e5ac..a597519 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -134,11 +134,11 @@ if !isempty(devices()) 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))" + Samplerate: 44100Hz + 2 channel sink: $(repr(output_name)) + 2 channel source: $(repr(input_name))""" + @test sprint(show, source) == "2 channel source: $(repr(input_name))" + @test sprint(show, sink) == "2 channel sink: $(repr(output_name))" write(stream, stream, 5s) @test PaErrorCode(handle_status(Pa_StopStream(stream.pointer_to))) == paNoError @test isopen(stream) @@ -209,8 +209,8 @@ if !isempty(devices()) big = typemax(Int) @test_throws DomainError( typemax(Int), - "$big exceeds maximum input channels for $output_name", - ) PortAudioStream(input_name, output_name, big, 0) + "$big exceeds maximum output channels for $output_name", + ) PortAudioStream(input_name, output_name, 0, big) @test_throws ArgumentError("Input or output must have at least 1 channel") PortAudioStream( input_name, output_name, @@ -219,8 +219,8 @@ if !isempty(devices()) adjust_channels = true, ) @test_throws ArgumentError(""" - Default sample rate 0 for input $output_name disagrees with - default sample rate 1 for output $input_name. + Default sample rate 0 for input \"$input_name\" disagrees with + default sample rate 1 for output \"$output_name\". Please specify a sample rate. """) combine_default_sample_rates( get_device(input_name),