2014-01-03 07:41:00 -08:00
|
|
|
module AudioIO
|
2013-12-11 20:18:36 -05:00
|
|
|
|
2013-12-30 03:29:43 -08:00
|
|
|
# export the basic API
|
2013-12-21 20:39:47 -05:00
|
|
|
export play
|
2013-12-22 17:27:29 -05:00
|
|
|
|
2013-12-21 20:39:47 -05:00
|
|
|
# default stream used when none is given
|
2013-12-22 16:45:16 -05:00
|
|
|
_stream = nothing
|
2013-12-21 20:39:47 -05:00
|
|
|
|
2013-12-22 16:45:16 -05:00
|
|
|
################## Types ####################
|
|
|
|
|
2013-12-22 17:16:53 -05:00
|
|
|
typealias AudioSample Float32
|
2013-12-22 16:45:16 -05:00
|
|
|
# A frame of audio, possibly multi-channel
|
2013-12-22 17:16:53 -05:00
|
|
|
typealias AudioBuf Array{AudioSample}
|
2013-12-22 16:45:16 -05:00
|
|
|
|
2013-12-22 18:06:02 -05:00
|
|
|
# A node in the render tree
|
|
|
|
abstract AudioNode
|
2013-12-22 16:45:16 -05:00
|
|
|
|
2013-12-30 01:00:04 -08:00
|
|
|
# A stream of audio (for instance that writes to hardware)
|
|
|
|
# All AudioStream subtypes should have a mixer and info field
|
|
|
|
abstract AudioStream
|
|
|
|
|
2013-12-22 16:45:16 -05:00
|
|
|
# Info about the hardware device
|
|
|
|
type DeviceInfo
|
|
|
|
sample_rate::Integer
|
|
|
|
buf_size::Integer
|
|
|
|
end
|
|
|
|
|
2013-12-22 18:06:02 -05:00
|
|
|
include("nodes.jl")
|
2014-01-03 11:47:38 -08:00
|
|
|
include("portaudio.jl")
|
2013-12-21 03:20:39 -05:00
|
|
|
|
2013-12-13 02:17:21 -05:00
|
|
|
############ Exported Functions #############
|
|
|
|
|
2013-12-30 01:00:04 -08:00
|
|
|
# TODO: we should have "stop" functions that remove nodes from the render tree
|
|
|
|
|
|
|
|
# Play an AudioNode by adding it as an input to the root mixer node
|
2013-12-22 17:27:29 -05:00
|
|
|
function play(node::AudioNode, stream::AudioStream)
|
2013-12-22 18:06:02 -05:00
|
|
|
# TODO: don't break demeter
|
2013-12-30 01:00:04 -08:00
|
|
|
append!(stream.mixer.mix_inputs, [node])
|
2013-12-22 18:13:57 -05:00
|
|
|
return nothing
|
2013-12-22 16:45:16 -05:00
|
|
|
end
|
|
|
|
|
2014-01-03 11:47:38 -08:00
|
|
|
# If the stream is not given, use the default global PortAudio stream
|
2013-12-22 17:27:29 -05:00
|
|
|
function play(node::AudioNode)
|
|
|
|
global _stream
|
|
|
|
if _stream == nothing
|
2014-01-03 11:47:38 -08:00
|
|
|
_stream = PortAudioStream()
|
2013-12-22 17:27:29 -05:00
|
|
|
end
|
|
|
|
play(node, _stream)
|
|
|
|
end
|
|
|
|
|
2013-12-30 01:00:04 -08:00
|
|
|
# Allow users to play a raw array by wrapping it in an ArrayPlayer
|
|
|
|
function play(arr::AudioBuf, args...)
|
2013-12-22 16:45:16 -05:00
|
|
|
player = ArrayPlayer(arr)
|
2013-12-30 01:00:04 -08:00
|
|
|
play(player, args...)
|
2013-12-21 20:39:47 -05:00
|
|
|
end
|
|
|
|
|
2013-12-30 01:00:04 -08:00
|
|
|
# If the array is the wrong floating type, convert it
|
|
|
|
function play{T <: FloatingPoint}(arr::Array{T}, args...)
|
|
|
|
arr = convert(AudioBuf, arr)
|
|
|
|
play(arr, args...)
|
|
|
|
end
|
|
|
|
|
|
|
|
# If the array is an integer type, scale to [-1, 1] floating point
|
|
|
|
|
|
|
|
# integer audio can be slightly (by 1) more negative than positive,
|
|
|
|
# so we just scale so that +/- typemax(T) becomes +/- 1
|
|
|
|
function play{T <: Signed}(arr::Array{T}, args...)
|
|
|
|
arr = arr / typemax(T)
|
|
|
|
play(arr, args...)
|
|
|
|
end
|
|
|
|
|
|
|
|
function play{T <: Unsigned}(arr::Array{T}, args...)
|
|
|
|
zero = (typemax(T) + 1) / 2
|
|
|
|
range = floor(typemax(T) / 2)
|
|
|
|
arr = (arr - zero) / range
|
|
|
|
play(arr, args...)
|
2013-12-21 20:39:47 -05:00
|
|
|
end
|
|
|
|
|
2014-01-03 07:41:00 -08:00
|
|
|
end # module AudioIO
|