diff --git a/.gitignore b/.gitignore index c375723..eb5f126 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *.o deps/usr deps/deps.jl +*.wav +*.flac diff --git a/src/sndfile.jl b/src/sndfile.jl index bf557be..9149c8b 100644 --- a/src/sndfile.jl +++ b/src/sndfile.jl @@ -5,6 +5,20 @@ const sndfile = "libsndfile" const SFM_READ = int32(0x10) const SFM_WRITE = int32(0x20) +const SF_FORMAT_WAV = 0x010000 +const SF_FORMAT_FLAC = 0x170000 +const SF_FORMAT_OGG = 0x200060 + +const SF_FORMAT_PCM_S8 = 0x0001 # Signed 8 bit data +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 EXT_TO_FORMAT = [ + ".wav" => SF_FORMAT_WAV, + ".flac" => SF_FORMAT_FLAC +] + type SF_INFO frames::Int64 samplerate::Int32 @@ -25,12 +39,28 @@ type AudioFile sfinfo::SF_INFO end -function af_open(path::String, mode::String="r") - # TODO: handle write/append modes +function af_open(path::String, mode::String = "r", + sampleRate::Integer = 44100, channels::Integer = 1, + format::Integer = 0) sfinfo = SF_INFO(0, 0, 0, 0, 0, 0) + file_mode = SFM_READ + + if mode == "w" + file_mode = SFM_WRITE + sfinfo.samplerate = sampleRate + sfinfo.channels = channels + if format == 0 + _, ext = splitext(path) + sfinfo.format = EXT_TO_FORMAT[ext] | SF_FORMAT_PCM_16 + else + sfinfo.format = format + end + end + filePtr = ccall((:sf_open, sndfile), Ptr{Void}, (Ptr{Uint8}, Int32, Ptr{SF_INFO}), - path, SFM_READ, &sfinfo) + path, file_mode, &sfinfo) + if filePtr == C_NULL errmsg = ccall((:sf_strerror, sndfile), Ptr{Uint8}, (Ptr{Void},), filePtr) error(bytestring(errmsg)) @@ -46,8 +76,8 @@ function Base.close(file::AudioFile) end end -function af_open(f::Function, path::String) - file = af_open(path) +function af_open(f::Function, args...) + file = af_open(args...) f(file) close(file) end @@ -89,6 +119,28 @@ function Base.read(file::AudioFile, nframes::Integer = file.sfinfo.frames, return arr end +function Base.write{T}(file::AudioFile, frames::Array{T}) + nframes = int(length(frames) / file.sfinfo.channels) + + if T == Int16 + return ccall((:sf_writef_short, sndfile), Int64, + (Ptr{Void}, Ptr{Int16}, Int64), + file.filePtr, frames, nframes) + elseif T == Int32 + return ccall((:sf_writef_int, sndfile), Int64, + (Ptr{Void}, Ptr{Int32}, Int64), + file.filePtr, frames, nframes) + elseif T == Float32 + return ccall((:sf_writef_float, sndfile), Int64, + (Ptr{Void}, Ptr{Float32}, Int64), + file.filePtr, frames, nframes) + elseif T == Float64 + return ccall((:sf_writef_double, sndfile), Int64, + (Ptr{Void}, Ptr{Float64}, Int64), + file.filePtr, frames, nframes) + end +end + type FilePlayer <: AudioNode active::Bool file::AudioFile diff --git a/test/sinwave.flac b/test/sinwave.flac deleted file mode 100644 index 739a247..0000000 Binary files a/test/sinwave.flac and /dev/null differ diff --git a/test/test_AudioIO.jl b/test/test_AudioIO.jl index 7d03f49..64de9d3 100644 --- a/test/test_AudioIO.jl +++ b/test/test_AudioIO.jl @@ -73,15 +73,25 @@ stop(node) process(test_stream) @test process(test_stream) == zeros(AudioIO.AudioSample, TEST_BUF_SIZE) -info("Testing libsndfile read") -samplerate = 44100 -freq = 440 -t = linspace(0, 2, 2 * samplerate) -phase = 2 * pi * freq * t -reference = int16((2 ^ 15 - 1) * sin(phase)) +for ext in ["wav", "flac"] + info("Testing $ext file write/read") -f = af_open("test/sinwave.flac") -@test f.sfinfo.channels == 1 -@test f.sfinfo.frames == 2 * samplerate -actual = read(f, 2 * samplerate) -@test_approx_eq(reference, actual) + fname = "test/sinwave.$ext" + + samplerate = 44100 + freq = 440 + t = [0 : 2 * samplerate - 1] / samplerate + phase = 2 * pi * freq * t + reference = int16((2 ^ 15 - 1) * sin(phase)) + + af_open(fname, "w") do f + write(f, reference) + end + + af_open(fname) do f + @test f.sfinfo.channels == 1 + @test f.sfinfo.frames == 2 * samplerate + actual = read(f, 2 * samplerate) + @test_approx_eq(reference, actual) + end +end