some minor tweaks and adds a couple convenience functions to file playback
This commit is contained in:
parent
7d7fd71341
commit
207fa26fd9
2 changed files with 144 additions and 18 deletions
101
file_api.md
Normal file
101
file_api.md
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
Some possible API concepts for dealing with files
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
|
||||||
|
* requires libflac for flac decoding
|
||||||
|
|
||||||
|
Use Cases
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Play a file through the speakers
|
||||||
|
* Use a file as input to an AudioNode for processing
|
||||||
|
* Read a file into an array
|
||||||
|
* Write an array into a file
|
||||||
|
* Write the output of an AudioNode to a file
|
||||||
|
|
||||||
|
|
||||||
|
IOStream API
|
||||||
|
------------
|
||||||
|
|
||||||
|
* users use standard julia "open" function to create an IOStream object
|
||||||
|
* FilePlayer <: AudioNode takes an IOStream and uses `sf_open_fd` to open and
|
||||||
|
play
|
||||||
|
* play(io::IOStream) creates a FilePlayer and plays it (just like ArrayPlayer)
|
||||||
|
* FileStream
|
||||||
|
|
||||||
|
### Play a file through the speakers
|
||||||
|
|
||||||
|
sndfile = open("myfile.wav")
|
||||||
|
play(sndfile)
|
||||||
|
close(sndfile)
|
||||||
|
|
||||||
|
### Use a file as input to an AudioNode for processing
|
||||||
|
|
||||||
|
sndfile = open("myfile.wav")
|
||||||
|
# maybe FilePlayer also takes a string input for convenience
|
||||||
|
node = FilePlayer(sndfile)
|
||||||
|
mixer = AudioMixer([node])
|
||||||
|
# etc.
|
||||||
|
|
||||||
|
### Read a file into an array
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
### Write an array into a file
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
### Write the output of an AudioNode to a file
|
||||||
|
|
||||||
|
node = SinOsc(440)
|
||||||
|
# ???
|
||||||
|
|
||||||
|
Separate Open Function API
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
* users use an explicit `af_open` function to open sound files
|
||||||
|
* `af_open` takes mode arguments just like the regular julia `open` function
|
||||||
|
* `af_open` returns a AudioFile instance.
|
||||||
|
|
||||||
|
### Play a file through the speakers
|
||||||
|
|
||||||
|
sndfile = af_open("myfile.wav")
|
||||||
|
play(sndfile)
|
||||||
|
close(sndfile)
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
play("myfile.wav")
|
||||||
|
|
||||||
|
### Use a file as input to an AudioNode for processing
|
||||||
|
|
||||||
|
sndfile = af_open("myfile.wav")
|
||||||
|
# FilePlayer also can take a string filename for convenience
|
||||||
|
node = FilePlayer(sndfile)
|
||||||
|
mixer = AudioMixer([node])
|
||||||
|
# etc.
|
||||||
|
|
||||||
|
### Read a file into an array
|
||||||
|
|
||||||
|
sndfile = af_open("myfile.wav")
|
||||||
|
vec = read(sndfile) # takes an optional arg for number of frames to read
|
||||||
|
close(sndfile)
|
||||||
|
|
||||||
|
### Write an array into a file
|
||||||
|
|
||||||
|
sndfile = af_open("myfile.wav", "w") #TODO: need to specify format
|
||||||
|
vec = rand(Float32, 441000) # 10 seconds of noise
|
||||||
|
write(sndfile, vec)
|
||||||
|
close(sndfile)
|
||||||
|
|
||||||
|
### Write the output of an AudioNode to a file
|
||||||
|
|
||||||
|
sndfile = af_open("myfile.wav", "w") #TODO: need to specify format
|
||||||
|
node = SinOsc(440)
|
||||||
|
write(sndfile, node, 44100) # record 1 second, optional block_size
|
||||||
|
# note that write() can handle sample depth conversions, and render() is
|
||||||
|
# called with the sampling rate of the file
|
||||||
|
close(sndfile)
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
export openAudio, closeAudio, readFrames, FileInput
|
export af_open, FilePlayer
|
||||||
|
|
||||||
const sndfile = "libsndfile"
|
const sndfile = "libsndfile"
|
||||||
|
|
||||||
|
|
||||||
const SFM_READ = int32(0x10)
|
const SFM_READ = int32(0x10)
|
||||||
const SFM_WRITE = int32(0x20)
|
const SFM_WRITE = int32(0x20)
|
||||||
|
|
||||||
|
@ -13,14 +12,21 @@ type SF_INFO
|
||||||
format::Int32
|
format::Int32
|
||||||
sections::Int32
|
sections::Int32
|
||||||
seekable::Int32
|
seekable::Int32
|
||||||
|
|
||||||
|
function SF_INFO(frames::Integer, samplerate::Integer, channels::Integer,
|
||||||
|
format::Integer, sections::Integer, seekable::Integer)
|
||||||
|
new(int64(frames), int32(samplerate), int32(channels), int32(format),
|
||||||
|
int32(sections), int32(seekable))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
type Sndfile
|
type AudioFile
|
||||||
filePtr::Ptr{Void}
|
filePtr::Ptr{Void}
|
||||||
sfinfo::SF_INFO
|
sfinfo::SF_INFO
|
||||||
end
|
end
|
||||||
|
|
||||||
function openAudio(path::String)
|
function af_open(path::String, mode::String="r")
|
||||||
|
# TODO: handle write/append modes
|
||||||
sfinfo = SF_INFO(0, 0, 0, 0, 0, 0)
|
sfinfo = SF_INFO(0, 0, 0, 0, 0, 0)
|
||||||
filePtr = ccall((:sf_open, sndfile), Ptr{Void},
|
filePtr = ccall((:sf_open, sndfile), Ptr{Void},
|
||||||
(Ptr{Uint8}, Int32, Ptr{SF_INFO}),
|
(Ptr{Uint8}, Int32, Ptr{SF_INFO}),
|
||||||
|
@ -30,24 +36,28 @@ function openAudio(path::String)
|
||||||
error(bytestring(errmsg))
|
error(bytestring(errmsg))
|
||||||
end
|
end
|
||||||
|
|
||||||
return Sndfile(filePtr, sfinfo)
|
return AudioFile(filePtr, sfinfo)
|
||||||
end
|
end
|
||||||
|
|
||||||
function closeAudio(file::Sndfile)
|
function Base.close(file::AudioFile)
|
||||||
err = ccall((:sf_close, sndfile), Int32, (Ptr{Void},), file.filePtr)
|
err = ccall((:sf_close, sndfile), Int32, (Ptr{Void},), file.filePtr)
|
||||||
if err != 0
|
if err != 0
|
||||||
error("Failed to close file")
|
error("Failed to close file")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function openAudio(f::Function, path::String)
|
function af_open(f::Function, path::String)
|
||||||
file = openAudio(path)
|
file = af_open(path)
|
||||||
f(file)
|
f(file)
|
||||||
closeAudio(file)
|
close(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
function readFrames(file::Sndfile, nframes::Integer, dtype::Type = Int16)
|
# TODO: we should implement a general read(node::AudioNode) that pulls data
|
||||||
|
# through an arbitrary render chain and returns the result as a vector
|
||||||
|
function Base.read(file::AudioFile, nframes::Integer = file.sfinfo.frames,
|
||||||
|
dtype::Type = Int16)
|
||||||
arr = []
|
arr = []
|
||||||
|
@assert file.sfinfo.channels <= 2
|
||||||
if file.sfinfo.channels == 2
|
if file.sfinfo.channels == 2
|
||||||
arr = zeros(dtype, 2, nframes)
|
arr = zeros(dtype, 2, nframes)
|
||||||
else
|
else
|
||||||
|
@ -79,24 +89,29 @@ function readFrames(file::Sndfile, nframes::Integer, dtype::Type = Int16)
|
||||||
return arr
|
return arr
|
||||||
end
|
end
|
||||||
|
|
||||||
type FileInput <: AudioNode
|
type FilePlayer <: AudioNode
|
||||||
active::Bool
|
active::Bool
|
||||||
file::Sndfile
|
file::AudioFile
|
||||||
|
|
||||||
function FileInput(path::String)
|
function FilePlayer(file::AudioFile)
|
||||||
node = new(false, openAudio(path))
|
node = new(false, file)
|
||||||
finalizer(node, node -> closeAudio(node.file))
|
finalizer(node, node -> close(node.file))
|
||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function FilePlayer(path::String)
|
||||||
|
return FilePlayer(af_open(path))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function render(node::FileInput, device_input::AudioBuf, info::DeviceInfo)
|
function render(node::FilePlayer, device_input::AudioBuf, info::DeviceInfo)
|
||||||
@assert node.file.sfinfo.samplerate == info.sample_rate
|
@assert node.file.sfinfo.samplerate == info.sample_rate
|
||||||
|
|
||||||
audio = readFrames(node.file, info.buf_size, AudioSample)
|
audio = read(node.file, info.buf_size, AudioSample)
|
||||||
|
|
||||||
if audio == Nothing
|
if audio == Nothing
|
||||||
return zeros(AudioSample, info.buf_size), false
|
node.active = false
|
||||||
|
return zeros(AudioSample, info.buf_size), node.active
|
||||||
end
|
end
|
||||||
|
|
||||||
# if the file is stereo, mix the two channels together
|
# if the file is stereo, mix the two channels together
|
||||||
|
@ -106,3 +121,13 @@ function render(node::FileInput, device_input::AudioBuf, info::DeviceInfo)
|
||||||
|
|
||||||
return audio, node.active
|
return audio, node.active
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function play(filename::String, args...)
|
||||||
|
player = FilePlayer(filename)
|
||||||
|
play(player, args...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function play(file::AudioFile, args...)
|
||||||
|
player = FilePlayer(file)
|
||||||
|
play(player, args...)
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in a new issue