more comments
This commit is contained in:
parent
8cd31c4522
commit
d836fe655d
1 changed files with 25 additions and 16 deletions
|
@ -303,6 +303,7 @@ function get_output_type(::Type{<:Union{SampledSignalsReader, SampledSignalsWrit
|
||||||
Int
|
Int
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# write a full chunk, starting from the middle of the julia buffer
|
||||||
function full_write!(buffer, julia_buffer, already)
|
function full_write!(buffer, julia_buffer, already)
|
||||||
chunk_frames = buffer.chunk_frames
|
chunk_frames = buffer.chunk_frames
|
||||||
@inbounds transpose!(
|
@inbounds transpose!(
|
||||||
|
@ -318,19 +319,21 @@ function (writer::SampledSignalsWriter)(buffer, (julia_buffer, offset, frame_cou
|
||||||
let buffer = buffer, julia_buffer = julia_buffer
|
let buffer = buffer, julia_buffer = julia_buffer
|
||||||
already -> full_write!(buffer, julia_buffer, already)
|
already -> full_write!(buffer, julia_buffer, already)
|
||||||
end,
|
end,
|
||||||
# keep going until there is less than a chunk left
|
# start at the offset, keep going until there is less than a chunk left
|
||||||
offset:chunk_frames:(offset + frame_count - chunk_frames),
|
offset:chunk_frames:(offset + frame_count - chunk_frames),
|
||||||
)
|
)
|
||||||
|
# write what's left
|
||||||
left = frame_count % chunk_frames
|
left = frame_count % chunk_frames
|
||||||
port_audio_range = 1:left
|
|
||||||
@inbounds transpose!(
|
@inbounds transpose!(
|
||||||
view(buffer.data, :, port_audio_range),
|
view(buffer.data, :, 1:left),
|
||||||
view(julia_buffer, port_audio_range .+ (offset + frame_count - left), :),
|
# the last left frames of the julia buffer
|
||||||
|
view(julia_buffer, (1:left) .+ (offset + frame_count - left), :),
|
||||||
)
|
)
|
||||||
write_buffer(buffer, left)
|
write_buffer(buffer, left)
|
||||||
frame_count
|
frame_count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# similar to above
|
||||||
function full_read!(buffer, julia_buffer, already)
|
function full_read!(buffer, julia_buffer, already)
|
||||||
chunk_frames = buffer.chunk_frames
|
chunk_frames = buffer.chunk_frames
|
||||||
read_buffer(buffer, chunk_frames)
|
read_buffer(buffer, chunk_frames)
|
||||||
|
@ -349,17 +352,17 @@ function (reader::SampledSignalsReader)(buffer, (julia_buffer, offset, frame_cou
|
||||||
offset:chunk_frames:(offset + frame_count - chunk_frames),
|
offset:chunk_frames:(offset + frame_count - chunk_frames),
|
||||||
)
|
)
|
||||||
left = frame_count % chunk_frames
|
left = frame_count % chunk_frames
|
||||||
port_audio_range = 1:left
|
|
||||||
read_buffer(buffer, left)
|
read_buffer(buffer, left)
|
||||||
@inbounds transpose!(
|
@inbounds transpose!(
|
||||||
view(julia_buffer, port_audio_range .+ (offset + frame_count - left), :),
|
view(julia_buffer, (1:left) .+ (offset + frame_count - left), :),
|
||||||
view(buffer.data, :, port_audio_range),
|
view(buffer.data, :, 1:left),
|
||||||
)
|
)
|
||||||
frame_count
|
frame_count
|
||||||
end
|
end
|
||||||
|
|
||||||
# a Buffer contains not just a buffer
|
# a Buffer contains not just a buffer
|
||||||
# but everything you might need for a source or sink
|
# but everything you might need for a source or sink
|
||||||
|
# hopefully, because it is immutable, the optimizer can just pass what it needs
|
||||||
struct Buffer{Sample, Scribe, InputType, OutputType}
|
struct Buffer{Sample, Scribe, InputType, OutputType}
|
||||||
stream_lock::ReentrantLock
|
stream_lock::ReentrantLock
|
||||||
pointer_to::Ptr{PaStream}
|
pointer_to::Ptr{PaStream}
|
||||||
|
@ -415,9 +418,11 @@ function buffer_task(
|
||||||
run_scribe(buffer)
|
run_scribe(buffer)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
# makes it able to run on a separate thread
|
||||||
task.sticky = false
|
task.sticky = false
|
||||||
if has_channels(buffer)
|
if has_channels(buffer)
|
||||||
schedule(task)
|
schedule(task)
|
||||||
|
# output channel will close when the task ends
|
||||||
bind(output_channel, task)
|
bind(output_channel, task)
|
||||||
else
|
else
|
||||||
close(input_channel)
|
close(input_channel)
|
||||||
|
@ -429,11 +434,6 @@ end
|
||||||
nchannels(buffer::Buffer) = buffer.number_of_channels
|
nchannels(buffer::Buffer) = buffer.number_of_channels
|
||||||
name(buffer::Buffer) = name(buffer.device)
|
name(buffer::Buffer) = name(buffer.device)
|
||||||
|
|
||||||
function close(buffer::Buffer)
|
|
||||||
close(buffer.inputs)
|
|
||||||
close(buffer.outputs)
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# PortAudioStream
|
# PortAudioStream
|
||||||
#
|
#
|
||||||
|
@ -510,7 +510,6 @@ function combine_default_sample_rates(
|
||||||
output_sample_rate,
|
output_sample_rate,
|
||||||
)
|
)
|
||||||
if input_sample_rate != output_sample_rate
|
if input_sample_rate != output_sample_rate
|
||||||
# TODO: just name
|
|
||||||
throw(
|
throw(
|
||||||
ArgumentError(
|
ArgumentError(
|
||||||
"""
|
"""
|
||||||
|
@ -535,7 +534,7 @@ function run_scribe(buffer)
|
||||||
input = try
|
input = try
|
||||||
take!(inputs)
|
take!(inputs)
|
||||||
catch an_error
|
catch an_error
|
||||||
# if the channel is closed, the scribe knows its done
|
# if the input channel is closed, the scribe knows its done
|
||||||
if an_error isa InvalidStateException && an_error.state === :closed
|
if an_error isa InvalidStateException && an_error.state === :closed
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
|
@ -670,6 +669,8 @@ function PortAudioStream(
|
||||||
PortAudioStream(
|
PortAudioStream(
|
||||||
sample_rate,
|
sample_rate,
|
||||||
pointer_to,
|
pointer_to,
|
||||||
|
# we need to keep track of the tasks
|
||||||
|
# so we can wait for them to finish and catch errors
|
||||||
buffer_task(
|
buffer_task(
|
||||||
stream_lock,
|
stream_lock,
|
||||||
pointer_to,
|
pointer_to,
|
||||||
|
@ -746,6 +747,9 @@ end
|
||||||
function close(stream::PortAudioStream)
|
function close(stream::PortAudioStream)
|
||||||
source_buffer = stream.source_buffer
|
source_buffer = stream.source_buffer
|
||||||
sink_buffer = stream.sink_buffer
|
sink_buffer = stream.sink_buffer
|
||||||
|
# closing is tricky, because we want to make sure we've read exactly as much as we've written
|
||||||
|
# but we have don't know exactly what the tasks are doing
|
||||||
|
# for now, just close one and then the other
|
||||||
if has_channels(source_buffer)
|
if has_channels(source_buffer)
|
||||||
# this will shut down the channels, which will shut down the threads
|
# this will shut down the channels, which will shut down the threads
|
||||||
close(source_buffer.inputs)
|
close(source_buffer.inputs)
|
||||||
|
@ -763,11 +767,12 @@ function close(stream::PortAudioStream)
|
||||||
handle_status(Pa_StopStream(pointer_to))
|
handle_status(Pa_StopStream(pointer_to))
|
||||||
end
|
end
|
||||||
handle_status(Pa_CloseStream(pointer_to))
|
handle_status(Pa_CloseStream(pointer_to))
|
||||||
|
# close the debug log and then read the file
|
||||||
|
# this will contain duplicate xrun warnings mentioned above
|
||||||
close(stream.debug_io)
|
close(stream.debug_io)
|
||||||
debug_log = open(stream.debug_file, "r") do io
|
debug_log = open(stream.debug_file, "r") do io
|
||||||
read(io, String)
|
read(io, String)
|
||||||
end
|
end
|
||||||
# this will contain duplicate xrun warnings mentioned above
|
|
||||||
if !isempty(debug_log)
|
if !isempty(debug_log)
|
||||||
@debug debug_log
|
@debug debug_log
|
||||||
end
|
end
|
||||||
|
@ -792,6 +797,8 @@ function eltype(
|
||||||
Sample
|
Sample
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# these defaults will error for non-sampledsignal scribes
|
||||||
|
# which is probably ok; we want these users to define new methods
|
||||||
read(stream::PortAudioStream, arguments...) = read(stream.source, arguments...)
|
read(stream::PortAudioStream, arguments...) = read(stream.source, arguments...)
|
||||||
read!(stream::PortAudioStream, arguments...) = read!(stream.source, arguments...)
|
read!(stream::PortAudioStream, arguments...) = read!(stream.source, arguments...)
|
||||||
write(stream::PortAudioStream, arguments...) = write(stream.sink, arguments...)
|
write(stream::PortAudioStream, arguments...) = write(stream.sink, arguments...)
|
||||||
|
@ -800,7 +807,7 @@ function write(sink::PortAudioStream, source::PortAudioStream, arguments...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function show(io::IO, stream::PortAudioStream)
|
function show(io::IO, stream::PortAudioStream)
|
||||||
# just show the first parameter (eltype)
|
# just show the first type parameter (eltype)
|
||||||
print(io, "PortAudioStream{")
|
print(io, "PortAudioStream{")
|
||||||
print(io, eltype(stream))
|
print(io, eltype(stream))
|
||||||
println(io, "}")
|
println(io, "}")
|
||||||
|
@ -875,6 +882,7 @@ name(source_or_sink::PortAudioSink) = name(source_or_sink.stream.sink_buffer)
|
||||||
name(source_or_sink::PortAudioSource) = name(source_or_sink.stream.source_buffer)
|
name(source_or_sink::PortAudioSource) = name(source_or_sink.stream.source_buffer)
|
||||||
|
|
||||||
# could show full type name, but the PortAudio part is probably redundant
|
# could show full type name, but the PortAudio part is probably redundant
|
||||||
|
# because these will usually only get printed as part of show for PortAudioStream
|
||||||
kind(::PortAudioSink) = "sink"
|
kind(::PortAudioSink) = "sink"
|
||||||
kind(::PortAudioSource) = "source"
|
kind(::PortAudioSource) = "source"
|
||||||
function show(io::IO, sink_or_source::Union{PortAudioSink, PortAudioSource})
|
function show(io::IO, sink_or_source::Union{PortAudioSink, PortAudioSource})
|
||||||
|
@ -897,6 +905,7 @@ function exchange(buffer, arguments...)
|
||||||
take!(buffer.outputs)
|
take!(buffer.outputs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# these will only work with sampledsignals scribes
|
||||||
function unsafe_write(
|
function unsafe_write(
|
||||||
sink::PortAudioSink{<:Buffer{<:Any, <:SampledSignalsWriter}},
|
sink::PortAudioSink{<:Buffer{<:Any, <:SampledSignalsWriter}},
|
||||||
julia_buffer::Array,
|
julia_buffer::Array,
|
||||||
|
|
Loading…
Add table
Reference in a new issue