massive restructuring. mono output to default sink works
This commit is contained in:
parent
80d329eb39
commit
fae9b38224
4 changed files with 363 additions and 286 deletions
1
REQUIRE
1
REQUIRE
|
@ -1,5 +1,6 @@
|
||||||
julia 0.4
|
julia 0.4
|
||||||
BinDeps
|
BinDeps
|
||||||
Compat
|
Compat
|
||||||
|
Devectorize
|
||||||
@osx Homebrew
|
@osx Homebrew
|
||||||
@windows WinRPM
|
@windows WinRPM
|
||||||
|
|
505
src/PortAudio.jl
505
src/PortAudio.jl
|
@ -1,261 +1,324 @@
|
||||||
module PortAudio
|
module PortAudio
|
||||||
using SampleTypes
|
using SampleTypes
|
||||||
using Compat
|
using Compat
|
||||||
|
using FixedPointNumbers
|
||||||
|
using Devectorize
|
||||||
|
using RingBuffers
|
||||||
|
|
||||||
# Get binary dependencies loaded from BinDeps
|
# Get binary dependencies loaded from BinDeps
|
||||||
include( "../deps/deps.jl")
|
include( "../deps/deps.jl")
|
||||||
include("libportaudio.jl")
|
include("libportaudio.jl")
|
||||||
|
|
||||||
# Info about the hardware device
|
export PortAudioSink, PortAudioSource
|
||||||
type DeviceInfo
|
|
||||||
sample_rate::Float32
|
|
||||||
buf_size::Integer
|
|
||||||
end
|
|
||||||
|
|
||||||
function devices()
|
# initialize PortAudio on module load
|
||||||
return get_portaudio_devices()
|
Pa_Initialize()
|
||||||
end
|
|
||||||
|
|
||||||
type PortAudioStream
|
type PortAudioDevice
|
||||||
info::DeviceInfo
|
name::UTF8String
|
||||||
show_warnings::Bool
|
host_api::UTF8String
|
||||||
stream::PaStream
|
|
||||||
|
|
||||||
function PortAudioStream(sample_rate=44100Hz,
|
|
||||||
buf_size::Integer=1024,
|
|
||||||
show_warnings::Bool=false)
|
|
||||||
# Pa_Initialize can be called multiple times, as long as each is
|
|
||||||
# paired with Pa_Terminate()
|
|
||||||
Pa_Initialize()
|
|
||||||
stream = Pa_OpenDefaultStream(2, 2, paFloat32, Int(sample_rate), buf_size)
|
|
||||||
Pa_StartStream(stream)
|
|
||||||
this = new(root, DeviceInfo(sample_rate, buf_size), show_warnings, stream)
|
|
||||||
@schedule(portaudio_task(this))
|
|
||||||
finalizer(this, close)
|
|
||||||
|
|
||||||
this
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
type PortAudioSink <: SampleSink
|
|
||||||
stream::PaStream
|
|
||||||
end
|
|
||||||
|
|
||||||
type PortAudioSource <: SampleSource
|
|
||||||
stream::PaStream
|
|
||||||
end
|
|
||||||
|
|
||||||
function close(stream::PortAudioStream)
|
|
||||||
Pa_StopStream(stream.stream)
|
|
||||||
Pa_CloseStream(stream.stream)
|
|
||||||
Pa_Terminate()
|
|
||||||
end
|
|
||||||
|
|
||||||
type Pa_StreamParameters
|
|
||||||
device::PaDeviceIndex
|
|
||||||
channelCount::Cint
|
|
||||||
sampleFormat::PaSampleFormat
|
|
||||||
suggestedLatency::PaTime
|
|
||||||
hostAPISpecificStreamInfo::Ptr{Void}
|
|
||||||
end
|
|
||||||
|
|
||||||
type PortAudioInterface <: AudioInterface
|
|
||||||
name::AbstractString
|
|
||||||
host_api::AbstractString
|
|
||||||
max_input_channels::Int
|
max_input_channels::Int
|
||||||
max_output_channels::Int
|
max_output_channels::Int
|
||||||
device_index::PaDeviceIndex
|
device_index::PaDeviceIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function devices()
|
||||||
|
ndevices = Pa_GetDeviceCount()
|
||||||
|
infos = PaDeviceInfo[Pa_GetDeviceInfo(i) for i in 0:(ndevices - 1)]
|
||||||
|
|
||||||
type Pa_AudioStream <: AudioStream
|
[PortAudioDevice(bytestring(d.name),
|
||||||
root::AudioMixer
|
bytestring(Pa_GetHostApiInfo(d.host_api).name),
|
||||||
info::DeviceInfo
|
d.max_input_channels,
|
||||||
show_warnings::Bool
|
d.max_output_channels,
|
||||||
|
i-1)
|
||||||
|
for (i, d) in enumerate(infos)]
|
||||||
|
end
|
||||||
|
|
||||||
|
# paramaterized on the sample type and sampling rate type
|
||||||
|
type PortAudioSink{T, U} <: SampleSink
|
||||||
stream::PaStream
|
stream::PaStream
|
||||||
sformat::PaSampleFormat
|
nchannels::Int
|
||||||
sbuffer::Array{Real}
|
samplerate::U
|
||||||
sbuffer_output_waiting::Integer
|
bufsize::Int
|
||||||
parent_may_use_buffer::Bool
|
ringbuf::RingBuffer
|
||||||
|
open::Bool
|
||||||
|
task::Task
|
||||||
|
|
||||||
"""
|
function PortAudioSink(eltype, rate, channels, bufsize)
|
||||||
Get device parameters needed for opening with portaudio
|
stream = Pa_OpenDefaultStream(0, channels, type_to_fmt[eltype], float(rate), bufsize)
|
||||||
default is input as 44100/16bit int, same as CD audio type input
|
writers = Condition[]
|
||||||
"""
|
# we want the ringbuf to output zeros to portaudio if it runs out of samples
|
||||||
function Pa_AudioStream(device_index, channels=2, input=false,
|
ringbuf = RingBuffer(eltype, RINGBUF_FRAMES, channels; underflow=PAD)
|
||||||
sample_rate::Integer=44100,
|
|
||||||
framesPerBuffer::Integer=2048,
|
|
||||||
show_warnings::Bool=false,
|
|
||||||
sample_format::PaSampleFormat=paInt16)
|
|
||||||
require_portaudio_init()
|
|
||||||
stream = Pa_OpenStream(device_index, channels, input, sample_format,
|
|
||||||
Cdouble(sample_rate), Culong(framesPerBuffer))
|
|
||||||
Pa_StartStream(stream)
|
Pa_StartStream(stream)
|
||||||
root = AudioMixer()
|
|
||||||
datatype = PaSampleFormat_to_T(sample_format)
|
this = new(stream, channels, rate, bufsize, ringbuf, true)
|
||||||
sbuf = ones(datatype, framesPerBuffer)
|
this.task = @schedule sinktask(this)
|
||||||
this = new(root, DeviceInfo(sample_rate, framesPerBuffer),
|
finalizer(this, close)
|
||||||
show_warnings, stream, sample_format, sbuf, 0, false)
|
yield()
|
||||||
info("Scheduling PortAudio Render Task...")
|
|
||||||
if input
|
|
||||||
@schedule(pa_input_task(this))
|
|
||||||
else
|
|
||||||
@schedule(pa_output_task(this))
|
|
||||||
end
|
|
||||||
this
|
this
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
PortAudioSink(eltype=Float32, rate=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE) =
|
||||||
Blocking read from a Pa_AudioStream that is open as input
|
PortAudioSink{eltype, typeof(rate)}(eltype, rate, channels, bufsize)
|
||||||
"""
|
|
||||||
function read_Pa_AudioStream(stream::Pa_AudioStream)
|
|
||||||
while true
|
const DEFAULT_BUFSIZE=4096
|
||||||
while stream.parent_may_use_buffer == false
|
const RINGBUF_FRAMES = 131072
|
||||||
sleep(0.001)
|
|
||||||
end
|
function Base.close(sink::PortAudioSink)
|
||||||
buffer = deepcopy(stream.sbuffer)
|
sink.open = false
|
||||||
stream.parent_may_use_buffer = false
|
# no task switches inside finalizer, not sure if we'll need this or not
|
||||||
return buffer
|
# wait(sink.task)
|
||||||
end
|
Pa_StopStream(sink.stream)
|
||||||
|
Pa_CloseStream(sink.stream)
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
|
||||||
Blocking write to a Pa_AudioStream that is open for output
|
SampleTypes.nchannels(sink::PortAudioSink) = sink.nchannels
|
||||||
"""
|
SampleTypes.samplerate(sink::PortAudioSink) = sink.samplerate
|
||||||
function write_Pa_AudioStream(stream::Pa_AudioStream, buffer)
|
Base.eltype{T, U}(sink::PortAudioSink{T, U}) = T
|
||||||
retval = 1
|
|
||||||
sbufsize = length(stream.sbuffer)
|
# type PortAudioSource <: SampleSource
|
||||||
inputlen = length(buffer)
|
# stream::PaStream
|
||||||
if(inputlen > sbufsize)
|
# end
|
||||||
info("Overflow at write_Pa_AudioStream")
|
|
||||||
retval = 0
|
function SampleTypes.unsafe_write(sink::PortAudioSink, buf::SampleBuf)
|
||||||
elseif(inputlen < sbufsize)
|
write(sink.ringbuf, buf)
|
||||||
info("Underflow at write_Pa_AudioStream")
|
|
||||||
retval = -1
|
|
||||||
end
|
|
||||||
while true
|
|
||||||
while stream.parent_may_use_buffer == false
|
|
||||||
sleep(0.001)
|
|
||||||
end
|
|
||||||
for idx in 1:min(sbufsize, inputlen)
|
|
||||||
stream.sbuffer[idx] = buffer[idx]
|
|
||||||
end
|
|
||||||
stream.parent_may_use_buffer = false
|
|
||||||
end
|
|
||||||
retval
|
|
||||||
end
|
end
|
||||||
|
|
||||||
############ Internal Functions ############
|
# function SampleTypes.unsafe_read!(sink::PortAudioSource, buf::SampleBuf)
|
||||||
|
# end
|
||||||
|
|
||||||
function portaudio_task(stream::PortAudioStream)
|
function sinktask(sink::PortAudioSink)
|
||||||
info("PortAudio Render Task Running...")
|
println("starting sink task")
|
||||||
n = bufsize(stream)
|
buffer = Array(eltype(sink), sink.bufsize, nchannels(sink))
|
||||||
buffer = zeros(AudioSample, n)
|
|
||||||
try
|
try
|
||||||
while true
|
while sink.open
|
||||||
while Pa_GetStreamReadAvailable(stream.stream) < n
|
total = Pa_GetStreamWriteAvailable(sink.stream)
|
||||||
sleep(0.005)
|
written = 0
|
||||||
|
while(written < total)
|
||||||
|
tocopy = min(size(buffer, 1), total - written)
|
||||||
|
read!(sink.ringbuf, sub(buffer, 1:tocopy, :))
|
||||||
|
# TODO: this will only work for mono streams
|
||||||
|
Pa_WriteStream(sink.stream, buffer, tocopy, true)
|
||||||
|
written += tocopy
|
||||||
end
|
end
|
||||||
Pa_ReadStream(stream.stream, buffer, n, stream.show_warnings)
|
|
||||||
# assume the root is always active
|
|
||||||
rendered = render(stream.root.renderer, buffer, stream.info)::AudioBuf
|
|
||||||
for i in 1:length(rendered)
|
|
||||||
buffer[i] = rendered[i]
|
|
||||||
end
|
|
||||||
for i in (length(rendered)+1):n
|
|
||||||
buffer[i] = 0.0
|
|
||||||
end
|
|
||||||
while Pa_GetStreamWriteAvailable(stream.stream) < n
|
|
||||||
sleep(0.005)
|
|
||||||
end
|
|
||||||
Pa_WriteStream(stream.stream, buffer, n, stream.show_warnings)
|
|
||||||
end
|
|
||||||
catch ex
|
|
||||||
warn("Audio Task died with exception: $ex")
|
|
||||||
Base.show_backtrace(STDOUT, catch_backtrace())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
"""
|
|
||||||
Get input device data, pass as a producer, no rendering
|
|
||||||
"""
|
|
||||||
function pa_input_task(stream::Pa_AudioStream)
|
|
||||||
info("PortAudio Input Task Running...")
|
|
||||||
n = bufsize(stream)
|
|
||||||
datatype = PaSampleFormat_to_T(stream.sformat)
|
|
||||||
# bigger ccall buffer to avoid overflow related errors
|
|
||||||
buffer = zeros(datatype, n * 8)
|
|
||||||
try
|
|
||||||
while true
|
|
||||||
while Pa_GetStreamReadAvailable(stream.stream) < n
|
|
||||||
sleep(0.005)
|
|
||||||
end
|
|
||||||
while stream.parent_may_use_buffer
|
|
||||||
sleep(0.005)
|
|
||||||
end
|
|
||||||
err = ccall((:Pa_ReadStream, libportaudio), PaError,
|
|
||||||
(PaStream, Ptr{Void}, Culong),
|
|
||||||
stream.stream, buffer, n)
|
|
||||||
handle_status(err, stream.show_warnings)
|
|
||||||
stream.sbuffer[1: n] = buffer[1: n]
|
|
||||||
stream.parent_may_use_buffer = true
|
|
||||||
sleep(0.005)
|
sleep(0.005)
|
||||||
end
|
end
|
||||||
catch ex
|
catch ex
|
||||||
warn("Audio Input Task died with exception: $ex")
|
warn("PortAudio Sink Task died with exception: $ex")
|
||||||
Base.show_backtrace(STDOUT, catch_backtrace())
|
Base.show_backtrace(STDOUT, catch_backtrace())
|
||||||
end
|
end
|
||||||
|
println("sink task finished")
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
# function sourcetask(sink::PortAudioSource)
|
||||||
Send output device data, no rendering
|
# while sink.open
|
||||||
"""
|
# end
|
||||||
function pa_output_task(stream::Pa_AudioStream)
|
#
|
||||||
info("PortAudio Output Task Running...")
|
# while Pa_GetStreamReadAvailable(stream.stream) < n
|
||||||
n = bufsize(stream)
|
# sleep(0.005)
|
||||||
try
|
# end
|
||||||
while true
|
# Pa_ReadStream(stream.stream, buffer, n, stream.show_warnings)
|
||||||
navail = stream.sbuffer_output_waiting
|
# # assume the root is always active
|
||||||
if navail > n
|
# rendered = render(stream.root.renderer, buffer, stream.info)::AudioBuf
|
||||||
info("Possible output buffer overflow in stream")
|
# for i in 1:length(rendered)
|
||||||
navail = n
|
# buffer[i] = rendered[i]
|
||||||
end
|
# end
|
||||||
if (navail > 1) & (stream.parent_may_use_buffer == false) &
|
# for i in (length(rendered)+1):n
|
||||||
(Pa_GetStreamWriteAvailable(stream.stream) < navail)
|
# buffer[i] = 0.0
|
||||||
Pa_WriteStream(stream.stream, stream.sbuffer,
|
# end
|
||||||
navail, stream.show_warnings)
|
# while Pa_GetStreamWriteAvailable(stream.stream) < n
|
||||||
stream.parent_may_use_buffer = true
|
# sleep(0.005)
|
||||||
else
|
# end
|
||||||
sleep(0.005)
|
# Pa_WriteStream(stream.stream, buffer, n, stream.show_warnings)
|
||||||
end
|
# end
|
||||||
end
|
|
||||||
catch ex
|
|
||||||
warn("Audio Output Task died with exception: $ex")
|
|
||||||
Base.show_backtrace(STDOUT, catch_backtrace())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_portaudio_devices()
|
|
||||||
require_portaudio_init()
|
|
||||||
device_count = ccall((:Pa_GetDeviceCount, libportaudio), PaDeviceIndex, ())
|
|
||||||
pa_devices = [ [Pa_GetDeviceInfo(i), i] for i in 0:(device_count - 1)]
|
|
||||||
[PortAudioInterface(bytestring(d[1].name),
|
|
||||||
bytestring(Pa_GetHostApiInfo(d[1].host_api).name),
|
|
||||||
d[1].max_input_channels,
|
|
||||||
d[1].max_output_channels,
|
|
||||||
d[2])
|
|
||||||
for d in pa_devices]
|
|
||||||
end
|
|
||||||
|
|
||||||
function require_portaudio_init()
|
|
||||||
# can be called multiple times with no effect
|
|
||||||
global portaudio_inited
|
|
||||||
if !portaudio_inited
|
|
||||||
info("Initializing PortAudio. Expect errors as we scan devices")
|
|
||||||
Pa_Initialize()
|
|
||||||
portaudio_inited = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end # module PortAudio
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# type Pa_AudioStream <: AudioStream
|
||||||
|
# root::AudioMixer
|
||||||
|
# info::DeviceInfo
|
||||||
|
# show_warnings::Bool
|
||||||
|
# stream::PaStream
|
||||||
|
# sformat::PaSampleFormat
|
||||||
|
# sbuffer::Array{Real}
|
||||||
|
# sbuffer_output_waiting::Integer
|
||||||
|
# parent_may_use_buffer::Bool
|
||||||
|
#
|
||||||
|
# """
|
||||||
|
# Get device parameters needed for opening with portaudio
|
||||||
|
# default is input as 44100/16bit int, same as CD audio type input
|
||||||
|
# """
|
||||||
|
# function Pa_AudioStream(device_index, channels=2, input=false,
|
||||||
|
# sample_rate::Integer=44100,
|
||||||
|
# framesPerBuffer::Integer=2048,
|
||||||
|
# show_warnings::Bool=false,
|
||||||
|
# sample_format::PaSampleFormat=paInt16)
|
||||||
|
# require_portaudio_init()
|
||||||
|
# stream = Pa_OpenStream(device_index, channels, input, sample_format,
|
||||||
|
# Cdouble(sample_rate), Culong(framesPerBuffer))
|
||||||
|
# Pa_StartStream(stream)
|
||||||
|
# root = AudioMixer()
|
||||||
|
# datatype = PaSampleFormat_to_T(sample_format)
|
||||||
|
# sbuf = ones(datatype, framesPerBuffer)
|
||||||
|
# this = new(root, DeviceInfo(sample_rate, framesPerBuffer),
|
||||||
|
# show_warnings, stream, sample_format, sbuf, 0, false)
|
||||||
|
# info("Scheduling PortAudio Render Task...")
|
||||||
|
# if input
|
||||||
|
# @schedule(pa_input_task(this))
|
||||||
|
# else
|
||||||
|
# @schedule(pa_output_task(this))
|
||||||
|
# end
|
||||||
|
# this
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# """
|
||||||
|
# Blocking read from a Pa_AudioStream that is open as input
|
||||||
|
# """
|
||||||
|
# function read_Pa_AudioStream(stream::Pa_AudioStream)
|
||||||
|
# while true
|
||||||
|
# while stream.parent_may_use_buffer == false
|
||||||
|
# sleep(0.001)
|
||||||
|
# end
|
||||||
|
# buffer = deepcopy(stream.sbuffer)
|
||||||
|
# stream.parent_may_use_buffer = false
|
||||||
|
# return buffer
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# """
|
||||||
|
# Blocking write to a Pa_AudioStream that is open for output
|
||||||
|
# """
|
||||||
|
# function write_Pa_AudioStream(stream::Pa_AudioStream, buffer)
|
||||||
|
# retval = 1
|
||||||
|
# sbufsize = length(stream.sbuffer)
|
||||||
|
# inputlen = length(buffer)
|
||||||
|
# if(inputlen > sbufsize)
|
||||||
|
# info("Overflow at write_Pa_AudioStream")
|
||||||
|
# retval = 0
|
||||||
|
# elseif(inputlen < sbufsize)
|
||||||
|
# info("Underflow at write_Pa_AudioStream")
|
||||||
|
# retval = -1
|
||||||
|
# end
|
||||||
|
# while true
|
||||||
|
# while stream.parent_may_use_buffer == false
|
||||||
|
# sleep(0.001)
|
||||||
|
# end
|
||||||
|
# for idx in 1:min(sbufsize, inputlen)
|
||||||
|
# stream.sbuffer[idx] = buffer[idx]
|
||||||
|
# end
|
||||||
|
# stream.parent_may_use_buffer = false
|
||||||
|
# end
|
||||||
|
# retval
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# ############ Internal Functions ############
|
||||||
|
#
|
||||||
|
# function portaudio_task(stream::PortAudioStream)
|
||||||
|
# info("PortAudio Render Task Running...")
|
||||||
|
# n = bufsize(stream)
|
||||||
|
# buffer = zeros(AudioSample, n)
|
||||||
|
# try
|
||||||
|
# while true
|
||||||
|
# while Pa_GetStreamReadAvailable(stream.stream) < n
|
||||||
|
# sleep(0.005)
|
||||||
|
# end
|
||||||
|
# Pa_ReadStream(stream.stream, buffer, n, stream.show_warnings)
|
||||||
|
# # assume the root is always active
|
||||||
|
# rendered = render(stream.root.renderer, buffer, stream.info)::AudioBuf
|
||||||
|
# for i in 1:length(rendered)
|
||||||
|
# buffer[i] = rendered[i]
|
||||||
|
# end
|
||||||
|
# for i in (length(rendered)+1):n
|
||||||
|
# buffer[i] = 0.0
|
||||||
|
# end
|
||||||
|
# while Pa_GetStreamWriteAvailable(stream.stream) < n
|
||||||
|
# sleep(0.005)
|
||||||
|
# end
|
||||||
|
# Pa_WriteStream(stream.stream, buffer, n, stream.show_warnings)
|
||||||
|
# end
|
||||||
|
# catch ex
|
||||||
|
# warn("Audio Task died with exception: $ex")
|
||||||
|
# Base.show_backtrace(STDOUT, catch_backtrace())
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# """
|
||||||
|
# Get input device data, pass as a producer, no rendering
|
||||||
|
# """
|
||||||
|
# function pa_input_task(stream::Pa_AudioStream)
|
||||||
|
# info("PortAudio Input Task Running...")
|
||||||
|
# n = bufsize(stream)
|
||||||
|
# datatype = PaSampleFormat_to_T(stream.sformat)
|
||||||
|
# # bigger ccall buffer to avoid overflow related errors
|
||||||
|
# buffer = zeros(datatype, n * 8)
|
||||||
|
# try
|
||||||
|
# while true
|
||||||
|
# while Pa_GetStreamReadAvailable(stream.stream) < n
|
||||||
|
# sleep(0.005)
|
||||||
|
# end
|
||||||
|
# while stream.parent_may_use_buffer
|
||||||
|
# sleep(0.005)
|
||||||
|
# end
|
||||||
|
# err = ccall((:Pa_ReadStream, libportaudio), PaError,
|
||||||
|
# (PaStream, Ptr{Void}, Culong),
|
||||||
|
# stream.stream, buffer, n)
|
||||||
|
# handle_status(err, stream.show_warnings)
|
||||||
|
# stream.sbuffer[1: n] = buffer[1: n]
|
||||||
|
# stream.parent_may_use_buffer = true
|
||||||
|
# sleep(0.005)
|
||||||
|
# end
|
||||||
|
# catch ex
|
||||||
|
# warn("Audio Input Task died with exception: $ex")
|
||||||
|
# Base.show_backtrace(STDOUT, catch_backtrace())
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# """
|
||||||
|
# Send output device data, no rendering
|
||||||
|
# """
|
||||||
|
# function pa_output_task(stream::Pa_AudioStream)
|
||||||
|
# info("PortAudio Output Task Running...")
|
||||||
|
# n = bufsize(stream)
|
||||||
|
# try
|
||||||
|
# while true
|
||||||
|
# navail = stream.sbuffer_output_waiting
|
||||||
|
# if navail > n
|
||||||
|
# info("Possible output buffer overflow in stream")
|
||||||
|
# navail = n
|
||||||
|
# end
|
||||||
|
# if (navail > 1) & (stream.parent_may_use_buffer == false) &
|
||||||
|
# (Pa_GetStreamWriteAvailable(stream.stream) < navail)
|
||||||
|
# Pa_WriteStream(stream.stream, stream.sbuffer,
|
||||||
|
# navail, stream.show_warnings)
|
||||||
|
# stream.parent_may_use_buffer = true
|
||||||
|
# else
|
||||||
|
# sleep(0.005)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# catch ex
|
||||||
|
# warn("Audio Output Task died with exception: $ex")
|
||||||
|
# Base.show_backtrace(STDOUT, catch_backtrace())
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
end # module PortAudio
|
||||||
|
|
|
@ -4,6 +4,16 @@
|
||||||
typealias PaTime Cdouble
|
typealias PaTime Cdouble
|
||||||
typealias PaError Cint
|
typealias PaError Cint
|
||||||
typealias PaSampleFormat Culong
|
typealias PaSampleFormat Culong
|
||||||
|
typealias PaDeviceIndex Cint
|
||||||
|
typealias PaHostApiIndex Cint
|
||||||
|
typealias PaHostApiTypeId Cint
|
||||||
|
# PaStream is always used as an opaque type, so we're always dealing
|
||||||
|
# with the pointer
|
||||||
|
typealias PaStream Ptr{Void}
|
||||||
|
typealias PaStreamCallback Void
|
||||||
|
typealias PaStreamFlags Culong
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const PA_NO_ERROR = 0
|
const PA_NO_ERROR = 0
|
||||||
const PA_INPUT_OVERFLOWED = -10000 + 19
|
const PA_INPUT_OVERFLOWED = -10000 + 19
|
||||||
|
@ -15,16 +25,38 @@ const paInt24 = PaSampleFormat(0x04)
|
||||||
const paInt16 = PaSampleFormat(0x08)
|
const paInt16 = PaSampleFormat(0x08)
|
||||||
const paInt8 = PaSampleFormat(0x10)
|
const paInt8 = PaSampleFormat(0x10)
|
||||||
const paUInt8 = PaSampleFormat(0x20)
|
const paUInt8 = PaSampleFormat(0x20)
|
||||||
|
const paNonInterleaved = PaSampleFormat(0x80000000)
|
||||||
|
|
||||||
@compat const pa_sample_formats = Dict{PaSampleFormat, Type}(
|
const fmt_to_type = Dict{PaSampleFormat, Type}(
|
||||||
1 => Float32
|
1 => Float32,
|
||||||
2 => Int32
|
2 => Int32,
|
||||||
4 => Int24
|
# 4 => Int24,
|
||||||
8 => Int16
|
8 => Int16,
|
||||||
16 => Int8
|
16 => Int8,
|
||||||
32 => UInt8
|
32 => UInt8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# const type_to_fmt = Dict{Type, PaSampleFormat}(
|
||||||
|
# Float32 => 1 | paNonInterleaved,
|
||||||
|
# Int32 => 2 | paNonInterleaved,
|
||||||
|
# # Int24 => 4 | paNonInterleaved,
|
||||||
|
# Int16 => 8 | paNonInterleaved,
|
||||||
|
# Int8 => 16 | paNonInterleaved,
|
||||||
|
# UInt8 => 3 | paNonInterleaved
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
|
||||||
|
# TODO: temporary for testing mono
|
||||||
|
const type_to_fmt = Dict{Type, PaSampleFormat}(
|
||||||
|
Float32 => 1,
|
||||||
|
Int32 => 2,
|
||||||
|
# Int24 =>,
|
||||||
|
Int16 => 8,
|
||||||
|
Int8 => 16,
|
||||||
|
UInt8 => 3
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
function Pa_Initialize()
|
function Pa_Initialize()
|
||||||
err = ccall((:Pa_Initialize, libportaudio), PaError, ())
|
err = ccall((:Pa_Initialize, libportaudio), PaError, ())
|
||||||
handle_status(err)
|
handle_status(err)
|
||||||
|
@ -50,9 +82,6 @@ end
|
||||||
# will range between 0 and Pa_GetHostApiCount() - 1. You can enumerate through
|
# will range between 0 and Pa_GetHostApiCount() - 1. You can enumerate through
|
||||||
# all the host APIs on the system by iterating through those values.
|
# all the host APIs on the system by iterating through those values.
|
||||||
|
|
||||||
typealias PaHostApiIndex Cint
|
|
||||||
typealias PaHostApiTypeId Cint
|
|
||||||
|
|
||||||
# PaHostApiTypeId values
|
# PaHostApiTypeId values
|
||||||
@compat const pa_host_api_names = Dict{PaHostApiTypeId, ASCIIString}(
|
@compat const pa_host_api_names = Dict{PaHostApiTypeId, ASCIIString}(
|
||||||
0 => "In Development", # use while developing support for a new host API
|
0 => "In Development", # use while developing support for a new host API
|
||||||
|
@ -82,10 +111,8 @@ end
|
||||||
|
|
||||||
Pa_GetHostApiInfo(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, libportaudio),
|
Pa_GetHostApiInfo(i) = unsafe_load(ccall((:Pa_GetHostApiInfo, libportaudio),
|
||||||
Ptr{PaHostApiInfo}, (PaHostApiIndex,), i))
|
Ptr{PaHostApiInfo}, (PaHostApiIndex,), i))
|
||||||
|
|
||||||
# Device Functions
|
|
||||||
|
|
||||||
typealias PaDeviceIndex Cint
|
# Device Functions
|
||||||
|
|
||||||
type PaDeviceInfo
|
type PaDeviceInfo
|
||||||
struct_version::Cint
|
struct_version::Cint
|
||||||
|
@ -100,66 +127,52 @@ type PaDeviceInfo
|
||||||
default_sample_rate::Cdouble
|
default_sample_rate::Cdouble
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Pa_GetDeviceCount() = ccall((:Pa_GetDeviceCount, libportaudio), PaDeviceIndex, ())
|
||||||
|
|
||||||
Pa_GetDeviceInfo(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, libportaudio),
|
Pa_GetDeviceInfo(i) = unsafe_load(ccall((:Pa_GetDeviceInfo, libportaudio),
|
||||||
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
|
Ptr{PaDeviceInfo}, (PaDeviceIndex,), i))
|
||||||
|
|
||||||
# Stream Functions
|
# Stream Functions
|
||||||
|
|
||||||
# PaStream is always used as an opaque type, so we're always dealing
|
type Pa_StreamParameters
|
||||||
# with the pointer
|
device::PaDeviceIndex
|
||||||
typealias PaStream Ptr{Void}
|
channelCount::Cint
|
||||||
typealias PaStreamCallback Void
|
sampleFormat::PaSampleFormat
|
||||||
typealias PaStreamFlags Culong
|
suggestedLatency::PaTime
|
||||||
|
hostAPISpecificStreamInfo::Ptr{Void}
|
||||||
function Pa_OpenDefaultStream(inChannels::Integer, outChannels::Integer,
|
|
||||||
sampleFormat::PaSampleFormat,
|
|
||||||
sampleRate::Real, framesPerBuffer::Integer)
|
|
||||||
streamPtr::Array{PaStream} = PaStream[0]
|
|
||||||
err = ccall((:Pa_OpenDefaultStream, libportaudio),
|
|
||||||
PaError, (Ptr{PaStream}, Cint, Cint,
|
|
||||||
PaSampleFormat, Cdouble, Culong,
|
|
||||||
Ptr{PaStreamCallback}, Ptr{Void}),
|
|
||||||
streamPtr, inChannels, outChannels, sampleFormat, sampleRate,
|
|
||||||
framesPerBuffer, 0, 0)
|
|
||||||
handle_status(err)
|
|
||||||
|
|
||||||
streamPtr[1]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
function Pa_OpenDefaultStream(inChannels, outChannels,
|
||||||
Open a single stream, not necessarily the default one
|
sampleFormat::PaSampleFormat,
|
||||||
The stream is unidirectional, either inout or default output
|
sampleRate, framesPerBuffer)
|
||||||
see http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html
|
streamPtr = Ref{PaStream}(0)
|
||||||
"""
|
err = ccall((:Pa_OpenDefaultStream, libportaudio),
|
||||||
function Pa_OpenStream(device::PaDeviceIndex,
|
PaError, (Ref{PaStream}, Cint, Cint,
|
||||||
channels::Cint, input::Bool,
|
PaSampleFormat, Cdouble, Culong,
|
||||||
sampleFormat::PaSampleFormat,
|
Ref{Void}, Ref{Void}),
|
||||||
sampleRate::Cdouble, framesPerBuffer::Culong)
|
streamPtr, inChannels, outChannels, sampleFormat, sampleRate,
|
||||||
streamPtr::Array{PaStream} = PaStream[0]
|
framesPerBuffer, C_NULL, C_NULL)
|
||||||
ioParameters = Pa_StreamParameters(device, channels,
|
|
||||||
sampleFormat, PaTime(0.001),
|
|
||||||
Ptr{Void}(0))
|
|
||||||
# CURRENTLY WORKING THIS OUT
|
|
||||||
if input
|
|
||||||
err = ccall((:Pa_OpenStream, libportaudio), PaError,
|
|
||||||
(PaStream,
|
|
||||||
Ptr{Pa_StreamParameters}, Ptr{Pa_StreamParameters},
|
|
||||||
Cdouble, Culong, PaStreamFlags,
|
|
||||||
Ptr{PaStreamCallback}, Ptr{Void}),
|
|
||||||
streamPtr, ioParameters, Ptr{Void}(0),
|
|
||||||
sampleRate, framesPerBuffer, 0,
|
|
||||||
Ptr{PaStreamCallback}(0), Ptr{Void}(0))
|
|
||||||
else
|
|
||||||
err = ccall((:Pa_OpenStream, libportaudio), PaError,
|
|
||||||
(PaStream, Ptr{Void}, Ref{Pa_StreamParameters},
|
|
||||||
Cdouble, Culong, Culong,
|
|
||||||
Ptr{PaStreamCallback}, Ptr{Void}),
|
|
||||||
streamPtr, Ptr{Void}(0), ioParameters,
|
|
||||||
sampleRate, framesPerBuffer, 0,
|
|
||||||
Ptr{PaStreamCallback}(0), Ptr{Void}(0))
|
|
||||||
end
|
|
||||||
handle_status(err)
|
handle_status(err)
|
||||||
streamPtr[1]
|
|
||||||
|
streamPtr[]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pa_OpenStream(inParams::Pa_StreamParameters,
|
||||||
|
outParams::Pa_StreamParameters,
|
||||||
|
sampleRate, framesPerBuffer,
|
||||||
|
flags::PaStreamFlags)
|
||||||
|
streamPtr = Ref{PaStream}(0)
|
||||||
|
err = ccall((:Pa_OpenStream, libportaudio), PaError,
|
||||||
|
(Ref{PaStream},
|
||||||
|
Ref{Pa_StreamParameters},
|
||||||
|
Ref{Pa_StreamParameters},
|
||||||
|
Cdouble, Culong, PaStreamFlags,
|
||||||
|
Ref{Void}, Ref{Void}),
|
||||||
|
streamPtr, inParams, outParams,
|
||||||
|
sampleRate, framesPerBuffer, flags,
|
||||||
|
C_NULL, C_NULL)
|
||||||
|
handle_status(err)
|
||||||
|
streamPtr[]
|
||||||
end
|
end
|
||||||
|
|
||||||
function Pa_StartStream(stream::PaStream)
|
function Pa_StartStream(stream::PaStream)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env julia
|
#!/usr/bin/env julia
|
||||||
|
|
||||||
@testset "No Tests" begin
|
@testset "PortAudio Tests" begin
|
||||||
@test false
|
@test false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue