From 289d7a56cd7ceceda7f8581075d8630742c93e1c Mon Sep 17 00:00:00 2001 From: Joris Kraak Date: Fri, 1 Aug 2014 14:00:24 +0200 Subject: [PATCH 1/5] Support playing sound from stereo files --- src/sndfile.jl | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/sndfile.jl b/src/sndfile.jl index 7664614..1b277be 100644 --- a/src/sndfile.jl +++ b/src/sndfile.jl @@ -86,11 +86,7 @@ end # through an arbitrary render chain and returns the result as a vector function Base.read(file::AudioFile, nframes::Integer, dtype::Type) @assert file.sfinfo.channels <= 2 - if file.sfinfo.channels == 2 - arr = zeros(dtype, 2, nframes) - else - arr = zeros(dtype, nframes) - end + arr = zeros(dtype, file.sfinfo.channels, nframes) if dtype == Int16 nread = ccall((:sf_readf_short, libsndfile), Int64, @@ -110,7 +106,7 @@ function Base.read(file::AudioFile, nframes::Integer, dtype::Type) file.filePtr, arr, nframes) end - return arr[1:nread] + return arr[:, 1:nread] end Base.read(file::AudioFile, dtype::Type) = Base.read(file, file.sfinfo.frames, dtype) @@ -157,11 +153,10 @@ FilePlayer(path::String) = FilePlayer(af_open(path)) function render(node::FileRenderer, device_input::AudioBuf, info::DeviceInfo) @assert node.file.sfinfo.samplerate == info.sample_rate - frames_read = 0 - audio = AudioSample[] + # Keep reading data from the file until the output buffer is full + audio = Array(AudioSample, node.file.sfinfo.channels, 0) while size(audio, 2) < info.buf_size - append!(audio, read(node.file, info.buf_size-size(audio, 2), AudioSample)) - println("read $(size(audio, 2)) frames, requested $(info.buf_size-size(audio, 2))") + audio = hcat(audio, read(node.file, info.buf_size-size(audio, 2), AudioSample)) end if audio == Nothing @@ -171,9 +166,9 @@ function render(node::FileRenderer, device_input::AudioBuf, info::DeviceInfo) # if the file is stereo, mix the two channels together if node.file.sfinfo.channels == 2 return (audio[1, :] / 2) + (audio[2, :] / 2) + else + return audio end - - return audio end function play(filename::String, args...) From 0cd33b82892818fbeaed0ea509e218b24fef5ce7 Mon Sep 17 00:00:00 2001 From: Joris Kraak Date: Fri, 1 Aug 2014 14:01:49 +0200 Subject: [PATCH 2/5] Stop trying to read from audio file if no data is available As soon as libsndfile has finished reading a file it no longer returns any data. This can send the `render` method for `FileRenderer` into an infinite loop. This way if not enough data is available a partial buffer is returned. --- src/sndfile.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sndfile.jl b/src/sndfile.jl index 1b277be..9fc257b 100644 --- a/src/sndfile.jl +++ b/src/sndfile.jl @@ -153,10 +153,13 @@ FilePlayer(path::String) = FilePlayer(af_open(path)) function render(node::FileRenderer, device_input::AudioBuf, info::DeviceInfo) @assert node.file.sfinfo.samplerate == info.sample_rate - # Keep reading data from the file until the output buffer is full + # Keep reading data from the file until the output buffer is full, but stop + # as soon as no more data can be read from the file audio = Array(AudioSample, node.file.sfinfo.channels, 0) - while size(audio, 2) < info.buf_size - audio = hcat(audio, read(node.file, info.buf_size-size(audio, 2), AudioSample)) + read_audio = zeros(AudioSample, node.file.sfinfo.channels, 1) + while size(audio, 2) < info.buf_size && size(read_audio, 2) > 0 + read_audio = read(node.file, info.buf_size-size(audio, 2), AudioSample) + audio = hcat(audio, read_audio) end if audio == Nothing From 19c33d71c6db6f7b9997da9825b3fef96a9ca314 Mon Sep 17 00:00:00 2001 From: Joris Kraak Date: Fri, 1 Aug 2014 14:04:38 +0200 Subject: [PATCH 3/5] Remove unnecessary value check Due to the way the `render` function was rewritten in eabf971, the audio variable is always going to be an array of `AudioSample`s, so a check to see if it is equal to `nothing` is no longer applicable. --- src/sndfile.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sndfile.jl b/src/sndfile.jl index 9fc257b..d302514 100644 --- a/src/sndfile.jl +++ b/src/sndfile.jl @@ -162,10 +162,6 @@ function render(node::FileRenderer, device_input::AudioBuf, info::DeviceInfo) audio = hcat(audio, read_audio) end - if audio == Nothing - return AudioSample[] - end - # if the file is stereo, mix the two channels together if node.file.sfinfo.channels == 2 return (audio[1, :] / 2) + (audio[2, :] / 2) From b79b47a4610065d78e3de532119365dfcbed75a0 Mon Sep 17 00:00:00 2001 From: Joris Kraak Date: Fri, 1 Aug 2014 14:06:14 +0200 Subject: [PATCH 4/5] Add audio file seek capabilities --- src/sndfile.jl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/sndfile.jl b/src/sndfile.jl index d302514..a40d811 100644 --- a/src/sndfile.jl +++ b/src/sndfile.jl @@ -1,4 +1,4 @@ -export af_open, FilePlayer +export af_open, FilePlayer, rewind const SFM_READ = int32(0x10) const SFM_WRITE = int32(0x20) @@ -12,6 +12,10 @@ const SF_FORMAT_PCM_16 = 0x0002 # Signed 16 bit data const SF_FORMAT_PCM_24 = 0x0003 # Signed 24 bit data const SF_FORMAT_PCM_32 = 0x0004 # Signed 32 bit data +const SF_SEEK_SET = 0 +const SF_SEEK_CUR = 1 +const SF_SEEK_END = 2 + const EXT_TO_FORMAT = [ ".wav" => SF_FORMAT_WAV, ".flac" => SF_FORMAT_FLAC @@ -76,6 +80,21 @@ function Base.close(file::AudioFile) end end +function Base.seek(file::AudioFile, offset::Integer, whence::Integer) + new_offset = ccall((:sf_seek, libsndfile), Int64, + (Ptr{Void}, Int64, Int32), file.filePtr, offset, whence) + + if new_offset < 0 + error("Could not seek to $(offset) in file") + end + + new_offset +end + +# Some convenience methods for easily navigating through a sound file +Base.seek(file::AudioFile, offset::Integer) = seek(file, offset, SF_SEEK_SET) +rewind(file::AudioFile) = seek(file, 0, SF_SEEK_SET) + function af_open(f::Function, args...) file = af_open(args...) f(file) From 26cf69ad949beb44cb83afbf256665529cf7084f Mon Sep 17 00:00:00 2001 From: Joris Kraak Date: Fri, 1 Aug 2014 17:56:01 +0200 Subject: [PATCH 5/5] Make audio file reads return data as n_frames x n_channels --- src/sndfile.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sndfile.jl b/src/sndfile.jl index a40d811..d08b192 100644 --- a/src/sndfile.jl +++ b/src/sndfile.jl @@ -105,7 +105,7 @@ end # through an arbitrary render chain and returns the result as a vector function Base.read(file::AudioFile, nframes::Integer, dtype::Type) @assert file.sfinfo.channels <= 2 - arr = zeros(dtype, file.sfinfo.channels, nframes) + arr = zeros(dtype, nframes, file.sfinfo.channels) if dtype == Int16 nread = ccall((:sf_readf_short, libsndfile), Int64, @@ -125,7 +125,7 @@ function Base.read(file::AudioFile, nframes::Integer, dtype::Type) file.filePtr, arr, nframes) end - return arr[:, 1:nread] + return arr[1:nread, :] end Base.read(file::AudioFile, dtype::Type) = Base.read(file, file.sfinfo.frames, dtype) @@ -174,11 +174,11 @@ function render(node::FileRenderer, device_input::AudioBuf, info::DeviceInfo) # Keep reading data from the file until the output buffer is full, but stop # as soon as no more data can be read from the file - audio = Array(AudioSample, node.file.sfinfo.channels, 0) - read_audio = zeros(AudioSample, node.file.sfinfo.channels, 1) - while size(audio, 2) < info.buf_size && size(read_audio, 2) > 0 - read_audio = read(node.file, info.buf_size-size(audio, 2), AudioSample) - audio = hcat(audio, read_audio) + audio = Array(AudioSample, 0, node.file.sfinfo.channels) + read_audio = zeros(AudioSample, 1, node.file.sfinfo.channels) + while size(audio, 1) < info.buf_size && size(read_audio, 1) > 0 + read_audio = read(node.file, info.buf_size-size(audio, 1), AudioSample) + audio = vcat(audio, read_audio) end # if the file is stereo, mix the two channels together