adds better source/sink show method and more docs/examples to README
This commit is contained in:
parent
e51c980f24
commit
64a08bc90f
3 changed files with 93 additions and 12 deletions
63
README.md
63
README.md
|
@ -1,18 +1,71 @@
|
||||||
PortAudio.jl
|
PortAudio.jl
|
||||||
============
|
============
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/JuliaAudio/PortAudio.jl.svg?branch=master)](https://travis-ci.org/JuliaAudio/PortAudio.jl)
|
|
||||||
[![codecov.io] (http://codecov.io/github/JuliaAudio/PortAudio.jl/coverage.svg?branch=master)] (http://codecov.io/github/JuliaAudio/PortAudio.jl?branch=master)
|
|
||||||
|
|
||||||
PortAudio.jl is a wrapper for [libportaudio](http://www.portaudio.com/), which gives cross-platform access to audio devices. It is compatible with the types defined in [SampleTypes.jl](https://github.com/JuliaAudio/SampleTypes.jl), so it provides `PortAudioSink` and `PortAudioSource` types, which can be read from and written to.
|
PortAudio.jl is a wrapper for [libportaudio](http://www.portaudio.com/), which gives cross-platform access to audio devices. It is compatible with the types defined in [SampleTypes.jl](https://github.com/JuliaAudio/SampleTypes.jl), so it provides `PortAudioSink` and `PortAudioSource` types, which can be read from and written to.
|
||||||
|
|
||||||
|
## Opening a source or sink
|
||||||
|
|
||||||
|
The easiest way to open a source or sink is with the default `PortAudioSource()` and `PortAudioSink()` constructors, which will open a 2-channel stream to your system's default devices. The constructors also take a variety of positional arguments:
|
||||||
|
|
||||||
|
```julia
|
||||||
|
function PortAudioSink(eltype=Float32, sr=48000Hz, channels=2, bufsize=4096)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can open a specific device by adding it as the first argument, either as a `PortAudioDevice` instance or by name.
|
||||||
|
|
||||||
|
```julia
|
||||||
|
function PortAudioSink(device::PortAudioDevice, args...)
|
||||||
|
function PortAudioSink(devname::AbstractString, args...)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can get a list of your system's devices with the `PortAudio.devices()` function:
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> PortAudio.devices()
|
||||||
|
6-element Array{PortAudio.PortAudioDevice,1}:
|
||||||
|
PortAudio.PortAudioDevice("AirPlay","Core Audio",0,2,0)
|
||||||
|
PortAudio.PortAudioDevice("Built-in Microph","Core Audio",2,0,1)
|
||||||
|
PortAudio.PortAudioDevice("Built-in Output","Core Audio",0,2,2)
|
||||||
|
PortAudio.PortAudioDevice("JackRouter","Core Audio",2,2,3)
|
||||||
|
PortAudio.PortAudioDevice("After Effects 13.5","Core Audio",0,0,4)
|
||||||
|
PortAudio.PortAudioDevice("Built-In Aggregate","Core Audio",2,2,5)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reading and Writing
|
||||||
|
|
||||||
|
`PortAudioSource` and `PortAudioSink` are subtypes of `SampleSource` and `SampleSink`, respectively (from [SampleTypes.jl](https://github.com/JuliaAudio/SampleTypes.jl)). This means they support all the stream and buffer features defined there. You can read to a buffer with `buf = read(source)`
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Set up an audio pass-through from microphone to speaker
|
### Set up an audio pass-through from microphone to speaker
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
src = PortAudioSource()
|
source = PortAudioSource()
|
||||||
sink = PortAudioSink()
|
sink = PortAudioSink()
|
||||||
write(sink, source)
|
write(sink, source)
|
||||||
end
|
```
|
||||||
|
|
||||||
|
### Open your built-in microphone and speaker by name
|
||||||
|
```julia
|
||||||
|
source = PortAudioSource("Built-in Microph")
|
||||||
|
sink = PortAudioSink("Built-in Output")
|
||||||
|
write(sink, source)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Record 10 seconds of audio and save to an ogg file
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> using FileIO, SampleTypes, LibSndFile
|
||||||
|
|
||||||
|
julia> source = PortAudioSource("Built-in Microph")
|
||||||
|
PortAudio.PortAudioSource{Float32,SIUnits.SIQuantity{Int64,0,0,-1,0,0,0,0,0,0}}("Built-in Microph")
|
||||||
|
2 channels sampled at 48000 s⁻¹
|
||||||
|
|
||||||
|
julia> buf = read(source, 10s)
|
||||||
|
480000-frame, 2-channel SampleBuf{Float32, 2, SIUnits.SIQuantity{Int64,0,0,-1,0,0,0,0,0,0}}
|
||||||
|
10.0 s at 48000 s⁻¹
|
||||||
|
▁▄▂▃▅▃▂▄▃▂▂▁▁▂▂▁▁▄▃▁▁▄▂▁▁▁▄▃▁▁▃▃▁▁▁▁▁▁▁▁▄▄▄▄▄▂▂▂▁▃▃▁▃▄▂▁▁▁▁▃▃▂▁▁▁▁▁▁▃▃▂▂▁▃▃▃▁▁▁▁
|
||||||
|
▁▄▂▃▅▃▂▄▃▂▂▁▁▂▂▁▁▄▃▁▁▄▂▁▁▁▄▃▁▁▃▃▁▁▁▁▁▁▁▁▄▄▄▄▄▂▂▂▁▃▃▁▃▄▂▁▁▁▁▃▃▂▁▁▁▁▁▁▃▃▂▂▁▃▃▃▁▁▁▁
|
||||||
|
|
||||||
|
julia> save(joinpath(homedir(), "Desktop", "myvoice.ogg"), buf)
|
||||||
```
|
```
|
||||||
|
|
|
@ -35,11 +35,17 @@ function devices()
|
||||||
for (i, d) in enumerate(infos)]
|
for (i, d) in enumerate(infos)]
|
||||||
end
|
end
|
||||||
|
|
||||||
# paramaterized on the sample type and sampling rate type
|
# not for external use, used in error message printing
|
||||||
|
devnames() = join(["\"$(dev.name)\"" for dev in devices()], "\n")
|
||||||
|
|
||||||
|
# Sources and sinks are mostly the same, so define them with this handy bit of
|
||||||
|
# metaprogramming
|
||||||
for (TypeName, Super) in ((:PortAudioSink, :SampleSink),
|
for (TypeName, Super) in ((:PortAudioSink, :SampleSink),
|
||||||
(:PortAudioSource, :SampleSource))
|
(:PortAudioSource, :SampleSource))
|
||||||
|
# paramaterized on the sample type and sampling rate type
|
||||||
@eval type $TypeName{T, U} <: $Super
|
@eval type $TypeName{T, U} <: $Super
|
||||||
stream::PaStream
|
stream::PaStream
|
||||||
|
name::UTF8String
|
||||||
samplerate::U
|
samplerate::U
|
||||||
jlbuf::Array{T, 2}
|
jlbuf::Array{T, 2}
|
||||||
pabuf::Array{T, 2}
|
pabuf::Array{T, 2}
|
||||||
|
@ -47,7 +53,7 @@ for (TypeName, Super) in ((:PortAudioSink, :SampleSink),
|
||||||
busy::Bool
|
busy::Bool
|
||||||
end
|
end
|
||||||
|
|
||||||
@eval function $TypeName(T, stream, sr, channels, bufsize)
|
@eval function $TypeName(T, stream, sr, channels, bufsize, name)
|
||||||
jlbuf = Array(T, bufsize, channels)
|
jlbuf = Array(T, bufsize, channels)
|
||||||
# as of portaudio 19.20140130 (which is the HomeBrew version as of 20160319)
|
# as of portaudio 19.20140130 (which is the HomeBrew version as of 20160319)
|
||||||
# noninterleaved data is not supported for the read/write interface on OSX
|
# noninterleaved data is not supported for the read/write interface on OSX
|
||||||
|
@ -57,7 +63,7 @@ for (TypeName, Super) in ((:PortAudioSink, :SampleSink),
|
||||||
|
|
||||||
Pa_StartStream(stream)
|
Pa_StartStream(stream)
|
||||||
|
|
||||||
this = $TypeName(stream, sr, jlbuf, pabuf, waiters, false)
|
this = $TypeName(stream, utf8(name), sr, jlbuf, pabuf, waiters, false)
|
||||||
finalizer(this, close)
|
finalizer(this, close)
|
||||||
|
|
||||||
this
|
this
|
||||||
|
@ -66,13 +72,13 @@ end
|
||||||
|
|
||||||
function PortAudioSink(eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
function PortAudioSink(eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
||||||
stream = Pa_OpenDefaultStream(0, channels, type_to_fmt[eltype], float(sr), bufsize)
|
stream = Pa_OpenDefaultStream(0, channels, type_to_fmt[eltype], float(sr), bufsize)
|
||||||
PortAudioSink(eltype, stream, sr, channels, bufsize)
|
PortAudioSink(eltype, stream, sr, channels, bufsize, "Default Sink")
|
||||||
end
|
end
|
||||||
|
|
||||||
function PortAudioSink(device::PortAudioDevice, eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
function PortAudioSink(device::PortAudioDevice, eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
||||||
params = Pa_StreamParameters(device.idx, channels, type_to_fmt[eltype], 0.0, C_NULL)
|
params = Pa_StreamParameters(device.idx, channels, type_to_fmt[eltype], 0.0, C_NULL)
|
||||||
stream = Pa_OpenStream(C_NULL, pointer_from_objref(params), float(sr), bufsize, paNoFlag)
|
stream = Pa_OpenStream(C_NULL, pointer_from_objref(params), float(sr), bufsize, paNoFlag)
|
||||||
PortAudioSink(eltype, stream, sr, channels, bufsize)
|
PortAudioSink(eltype, stream, sr, channels, bufsize, device.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function PortAudioSink(devname::AbstractString, args...)
|
function PortAudioSink(devname::AbstractString, args...)
|
||||||
|
@ -81,17 +87,18 @@ function PortAudioSink(devname::AbstractString, args...)
|
||||||
return PortAudioSink(d, args...)
|
return PortAudioSink(d, args...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
error("No PortAudio device matching \"$devname\" found.\nAvailable Devices:\n$(devnames())")
|
||||||
end
|
end
|
||||||
|
|
||||||
function PortAudioSource(eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
function PortAudioSource(eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
||||||
stream = Pa_OpenDefaultStream(channels, 0, type_to_fmt[eltype], float(sr), bufsize)
|
stream = Pa_OpenDefaultStream(channels, 0, type_to_fmt[eltype], float(sr), bufsize)
|
||||||
PortAudioSource(eltype, stream, sr, channels, bufsize)
|
PortAudioSource(eltype, stream, sr, channels, bufsize, "Default Source")
|
||||||
end
|
end
|
||||||
|
|
||||||
function PortAudioSource(device::PortAudioDevice, eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
function PortAudioSource(device::PortAudioDevice, eltype=Float32, sr=48000Hz, channels=2, bufsize=DEFAULT_BUFSIZE)
|
||||||
params = Pa_StreamParameters(device.idx, channels, type_to_fmt[eltype], 0.0, C_NULL)
|
params = Pa_StreamParameters(device.idx, channels, type_to_fmt[eltype], 0.0, C_NULL)
|
||||||
stream = Pa_OpenStream(pointer_from_objref(params), C_NULL, float(sr), bufsize, paNoFlag)
|
stream = Pa_OpenStream(pointer_from_objref(params), C_NULL, float(sr), bufsize, paNoFlag)
|
||||||
PortAudioSource(eltype, stream, sr, channels, bufsize)
|
PortAudioSource(eltype, stream, sr, channels, bufsize, device.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function PortAudioSource(devname::AbstractString, args...)
|
function PortAudioSource(devname::AbstractString, args...)
|
||||||
|
@ -100,13 +107,20 @@ function PortAudioSource(devname::AbstractString, args...)
|
||||||
return PortAudioSource(d, args...)
|
return PortAudioSource(d, args...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
error("No PortAudio device matching \"$devname\" found.\nAvailable Devices:\n$(devnames())")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# most of these methods are the same for Sources and Sinks, so define them on
|
# most of these methods are the same for Sources and Sinks, so define them on
|
||||||
# the union
|
# the union
|
||||||
typealias PortAudioStream{T, U} Union{PortAudioSink{T, U}, PortAudioSource{T, U}}
|
typealias PortAudioStream{T, U} Union{PortAudioSink{T, U}, PortAudioSource{T, U}}
|
||||||
|
|
||||||
|
function Base.show{T <: PortAudioStream}(io::IO, stream::T)
|
||||||
|
println(io, T, "(\"", stream.name, "\")")
|
||||||
|
print(io, nchannels(stream), " channels sampled at ", samplerate(stream))
|
||||||
|
end
|
||||||
|
|
||||||
function Base.close(stream::PortAudioStream)
|
function Base.close(stream::PortAudioStream)
|
||||||
Pa_StopStream(stream.stream)
|
Pa_StopStream(stream.stream)
|
||||||
Pa_CloseStream(stream.stream)
|
Pa_CloseStream(stream.stream)
|
||||||
|
|
|
@ -121,6 +121,13 @@ type Pa_StreamParameters
|
||||||
hostAPISpecificStreamInfo::Ptr{Void}
|
hostAPISpecificStreamInfo::Ptr{Void}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
type PaStreamInfo
|
||||||
|
structVersion::Cint
|
||||||
|
inputLatency::PaTime
|
||||||
|
outputLatency::PaTime
|
||||||
|
sampleRate::Cdouble
|
||||||
|
end
|
||||||
|
|
||||||
function Pa_OpenDefaultStream(inChannels, outChannels,
|
function Pa_OpenDefaultStream(inChannels, outChannels,
|
||||||
sampleFormat::PaSampleFormat,
|
sampleFormat::PaSampleFormat,
|
||||||
sampleRate, framesPerBuffer)
|
sampleRate, framesPerBuffer)
|
||||||
|
@ -206,6 +213,13 @@ function Pa_WriteStream(stream::PaStream, buf::Array, frames::Integer=length(buf
|
||||||
nothing
|
nothing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Pa_GetStreamInfo(stream::PaStream)
|
||||||
|
infoptr = ccall((:Pa_GetStreamInfo, libportaudio), Ptr{PaStreamInfo},
|
||||||
|
(PaStream, ), stream)
|
||||||
|
|
||||||
|
unsafe_load(infoptr)
|
||||||
|
end
|
||||||
|
|
||||||
# General utility function to handle the status from the Pa_* functions
|
# General utility function to handle the status from the Pa_* functions
|
||||||
function handle_status(err::PaError, show_warnings::Bool=true)
|
function handle_status(err::PaError, show_warnings::Bool=true)
|
||||||
if err == PA_OUTPUT_UNDERFLOWED || err == PA_INPUT_OVERFLOWED
|
if err == PA_OUTPUT_UNDERFLOWED || err == PA_INPUT_OVERFLOWED
|
||||||
|
|
Loading…
Reference in a new issue