removes flac test as it was broken on osx, adds get_audio_devices

This commit is contained in:
Spencer Russell 2014-05-02 16:10:54 -04:00
parent 81a8503c1d
commit 0d84cb409b
5 changed files with 147 additions and 94 deletions

21
deps/src/shim.c vendored
View file

@ -3,7 +3,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
static int paCallback(const void *inputBuffer, void *outputBuffer, int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
@ -61,29 +61,12 @@ PaError open_stream(unsigned int sampleRate, unsigned int bufSize)
return paNoError; return paNoError;
} }
//PaError stop_sin(void)
//{
// PaError err;
// err = Pa_StopStream(sin_stream);
// if(err != paNoError)
// {
// return err;
// }
//
// err = Pa_CloseStream(sin_stream);
// if( err != paNoError )
// {
// return err;
// }
// return paNoError;
//}
/* /*
* This routine will be called by the PortAudio engine when audio is needed. * This routine will be called by the PortAudio engine when audio is needed.
* It may called at interrupt level on some machines so don't do anything that * It may called at interrupt level on some machines so don't do anything that
* could mess up the system like calling malloc() or free(). * could mess up the system like calling malloc() or free().
*/ */
static int paCallback(const void *inputBuffer, void *outputBuffer, int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,

View file

@ -1,7 +1,7 @@
module AudioIO module AudioIO
# export the basic API # export the basic API
export play, stop export play, stop, get_audio_devices
# default stream used when none is given # default stream used when none is given
_stream = nothing _stream = nothing
@ -47,34 +47,6 @@ function play(node::AudioNode)
play(node, _stream) play(node, _stream)
end end
# Allow users to play a raw array by wrapping it in an ArrayPlayer
function play(arr::AudioBuf, args...)
player = ArrayPlayer(arr)
play(player, args...)
end
# 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...)
end
function stop(node::AudioNode) function stop(node::AudioNode)
deactivate(node) deactivate(node)
node node
@ -99,4 +71,8 @@ function Base.wait(node::AudioNode)
end end
end end
function get_audio_devices()
return get_portaudio_devices()
end
end # module AudioIO end # module AudioIO

View file

@ -17,7 +17,7 @@ end
function render(node::SinOsc, device_input::AudioBuf, info::DeviceInfo) function render(node::SinOsc, device_input::AudioBuf, info::DeviceInfo)
phase = AudioSample[1:info.buf_size] * 2pi * node.freq / info.sample_rate phase = AudioSample[1:info.buf_size] * 2pi * node.freq / info.sample_rate
phase += node.phase phase .+= node.phase
node.phase = phase[end] node.phase = phase[end]
return sin(phase), is_active(node) return sin(phase), is_active(node)
end end
@ -122,6 +122,34 @@ function render(node::ArrayPlayer, device_input::AudioBuf, info::DeviceInfo)
return output, is_active(node) return output, is_active(node)
end end
# Allow users to play a raw array by wrapping it in an ArrayPlayer
function play(arr::AudioBuf, args...)
player = ArrayPlayer(arr)
play(player, args...)
end
# 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...)
end
#### AudioInput #### #### AudioInput ####
# Renders incoming audio input from the hardware # Renders incoming audio input from the hardware

View file

@ -2,11 +2,33 @@ typealias PaTime Cdouble
typealias PaError Cint typealias PaError Cint
typealias PaSampleFormat Culong typealias PaSampleFormat Culong
typealias PaStream Void typealias PaStream Void
typealias PaDeviceIndex Cint
typealias PaHostApiIndex Cint
typealias PaTime Cdouble
typealias PaHostApiTypeId Cint
const PA_NO_ERROR = 0 const PA_NO_ERROR = 0
const libportaudio_shim = find_library(["libportaudio_shim",], const libportaudio_shim = find_library(["libportaudio_shim",],
[Pkg.dir("AudioIO", "deps", "usr", "lib"),]) [Pkg.dir("AudioIO", "deps", "usr", "lib"),])
# PaHostApiTypeId values
const pa_host_api_names = {
0 => "In Development", # use while developing support for a new host API
1 => "Direct Sound",
2 => "MME",
3 => "ASIO",
4 => "Sound Manager",
5 => "Core Audio",
7 => "OSS",
8 => "ALSA",
9 => "AL",
10 => "BeOS",
11 => "WDMKS",
12 => "Jack",
13 => "WASAPI",
14 => "AudioScience HPI"
}
# track whether we've already inited PortAudio # track whether we've already inited PortAudio
portaudio_inited = false portaudio_inited = false
@ -17,15 +39,7 @@ type PortAudioStream <: AudioStream
info::DeviceInfo info::DeviceInfo
function PortAudioStream(sample_rate::Int=44100, buf_size::Int=1024) function PortAudioStream(sample_rate::Int=44100, buf_size::Int=1024)
global portaudio_inited
if !portaudio_inited
@assert(libportaudio_shim != "", "Failed to find required library libportaudio_shim. Try re-running the package script using Pkg.build(\"AudioIO\"), then reloading with reload(\"AudioIO\")")
init_portaudio() init_portaudio()
portaudio_inited = true
else
error("Currently only 1 stream is supported at a time")
end
mixer = AudioMixer() mixer = AudioMixer()
stream = new(mixer, DeviceInfo(sample_rate, buf_size)) stream = new(mixer, DeviceInfo(sample_rate, buf_size))
# we need to start up the stream with the portaudio library # we need to start up the stream with the portaudio library
@ -40,12 +54,6 @@ function synchronize_buffer(buffer)
ccall((:synchronize_buffer, libportaudio_shim), Void, (Ptr{Void},), buffer) ccall((:synchronize_buffer, libportaudio_shim), Void, (Ptr{Void},), buffer)
end end
function init_portaudio()
info("Initializing PortAudio. Expect errors as we scan devices")
err = ccall((:Pa_Initialize, "libportaudio"), PaError, ())
handle_status(err)
end
function open_portaudio_stream(stream::PortAudioStream) function open_portaudio_stream(stream::PortAudioStream)
# starts up a stream with the portaudio library and associates it with the # starts up a stream with the portaudio library and associates it with the
# given AudioIO PortAudioStream # given AudioIO PortAudioStream
@ -55,10 +63,7 @@ function open_portaudio_stream(stream::PortAudioStream)
fd = ccall((:make_pipe, libportaudio_shim), Cint, ()) fd = ccall((:make_pipe, libportaudio_shim), Cint, ())
info("Launching PortAudio Task...") info("Launching PortAudio Task...")
function task_wrapper() schedule(Task(() -> portaudio_task(fd, stream)))
portaudio_task(fd, stream)
end
schedule(Task(task_wrapper))
# TODO: test not yielding here # TODO: test not yielding here
yield() yield()
info("Audio Task Yielded, starting the stream...") info("Audio Task Yielded, starting the stream...")
@ -108,6 +113,65 @@ function portaudio_task(jl_filedesc::Integer, stream::PortAudioStream)
end end
end end
type PaDeviceInfo
struct_version::Cint
name::Ptr{Cchar}
host_api::PaHostApiIndex
max_input_channels::Cint
max_output_channels::Cint
default_low_input_latency::PaTime
default_low_output_latency::PaTime
default_high_input_latency::PaTime
default_high_output_latency::PaTime
default_sample_rate::Cdouble
end
type PaHostApiInfo
struct_version::Cint
api_type::PaHostApiTypeId
name::Ptr{Cchar}
deviceCount::Cint
defaultInputDevice::PaDeviceIndex
defaultOutputDevice::PaDeviceIndex
end
type AudioDevice
name::String
host_api::String
max_input_channels::Int
max_output_channels::Int
end
# some thin wrappers to portaudio calls
get_device_info(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, "libportaudio"),
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
get_host_api_info(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, "libportaudio"),
Ptr{PaHostApiInfo}, (PaHostApiIndex,), i))
function get_portaudio_devices()
init_portaudio()
device_count = ccall((:Pa_GetDeviceCount, "libportaudio"), PaDeviceIndex, ())
pa_devices = [get_device_info(i) for i in 0:(device_count - 1)]
[AudioDevice(bytestring(d.name),
bytestring(get_host_api_info(d.host_api).name),
d.max_input_channels,
d.max_output_channels)
for d in pa_devices]
end
function init_portaudio()
# can be called multiple times with no effect
global portaudio_inited
if !portaudio_inited
@assert(libportaudio_shim != "", "Failed to find required library libportaudio_shim. Try re-running the package script using Pkg.build(\"AudioIO\"), then reloading with reload(\"AudioIO\")")
info("Initializing PortAudio. Expect errors as we scan devices")
err = ccall((:Pa_Initialize, "libportaudio"), PaError, ())
handle_status(err)
portaudio_inited = true
end
end
# Old code for reference during initial development. We can get rid of this # Old code for reference during initial development. We can get rid of this
# once the library is a little more mature # once the library is a little more mature

View file

@ -73,10 +73,9 @@ stop(node)
process(test_stream) process(test_stream)
@test process(test_stream) == zeros(AudioIO.AudioSample, TEST_BUF_SIZE) @test process(test_stream) == zeros(AudioIO.AudioSample, TEST_BUF_SIZE)
for ext in ["wav", "flac"] info("Testing wav file write/read")
info("Testing $ext file write/read")
fname = "test/sinwave.$ext" fname = "test/sinwave.wav"
samplerate = 44100 samplerate = 44100
freq = 440 freq = 440
@ -94,4 +93,7 @@ for ext in ["wav", "flac"]
actual = read(f, 2 * samplerate) actual = read(f, 2 * samplerate)
@test_approx_eq(reference, actual) @test_approx_eq(reference, actual)
end end
end
info("Testing Audio Device Listing...")
d_list = get_audio_devices()
@test length(d_list) > 0