Documentation
NAME
Audio::PortAudio - Access to audio input and output devices
SYNOPSIS
use Audio::PortAudio;
my $pa = Audio::PortAudio.new;
# get the default stream with no inputs, 2 output channels
# for audio encoded as 32 bit floats at 44100 samplerate;
my $stream = $pa.open-default-stream(0,2,Audio::PortAudio::Float32,44100);
$stream.start;
loop {
# get some audio data in a carray from somewhere
$stream.write($carray, $frame-count);
}
See also the examples directory in the distribution
DESCRIPTION
This module provides a mechanism to get audio into and out of your program via a sound card or some other sub-system supported by the Portaudio library (http://www.portaudio.com/), this may include "ALSA", "JACK" or "OSS" on Linux, "CoreAudio" on Mac and "ASIO" on Windows, (of course the actual support depends on how the library was built on your system.)
You will need to have the portaudio library installed on your system to use this, it may be available as a package or come pre-installed, but the details will be specific to your platform.
The interface is somewhat simplified in comparison to the underlying library and in particular only "blocking" IO is supported at the current time (though this does not preclude the use of the callback API in the future, it's just an interface that is natural to a Raku developer doesn't suggest itself at the moment.)
It is important to note that the constraints of real-time audio data handling mean that you have to be careful that you allow for consistent and timely handing of the data to or from the device for proper results, you may find that for some applications you will need to avoid the use of any concurrency whatsoever for instance (the streaming example is such a case where the time budget was such that any unexpected garbage collection or other processor stealing activity didn't leave the process enough time spare to recover and the stream eventually became unusable.)
Also it should be noted that some types of source API ("JACK" in particular,) require that you use a fixed buffer size that is consistent with that configured for the host service, unfortunately portaudio doesn't appear to provide a way of discovering this so you may need to either check with the source configuration or experiment to find a correct and working value for buffer sizes. The symptoms of this may include choppy, "syncopated" or "phased" output.
This is based on the original work of Peschwa (https://github.com/peschwa/Audio-PortAudio) which I forked and then just completely hijacked when I realised it could be potentially be made useful :) So most of the credit probably goes to him.
METHODS
method new
method new()
The constructor doesn't currently take any arguments. It will cause initialize()
for you.
method version
method version( --> Str )
This returns the portaudio library version string that may be useful for diagnostic purposes.
method initialize
method initialize( --> Bool )
This starts the portaudio service and will initialise all of the host API drivers found, which may (depending on the configuration,) cause the drivers to emit some output (typically ALSA and JACK will do this.) You probably don't need to call this yourself as it is called by the constructor, though may be necessary after terminate
if you didn't actually end your program. If there was a problem initializing the library then an exception will be thrown.
method terminate
method terminate( --> Int )
This ends the portaudio and will shutdown all the backends, calling any methods except initialise()
after this will give rise to an exception.
method device-count
method device-count( --> Int )
This returns the number of devices in the system.
method device-info
method device-info(Int $device-number --> DeviceInfo )
This returns the Audio::PortAudio::DeviceInfo
object for the device index
which is in the range 0 to device-count
exclusive. If the device index is out of range or there is some other error an exception will be thrown.
method devices
method devices()
This is a convenience to return a lazy list of the devices as Audio::PortAudio::DeviceInfo
objects, it may be useful to enumerate the devices but you will need to keep track of the index in order to be able to open a stream with a particular device.
method host-api-index
method host-api-index(HostApiTypeId $type --> Int )
This returns the index number of the host API as used in the Audio::PortAudio::DeviceInfo
given the host API type, and can be used to enumerate devices of a certain type from the devices
list. HostApiTypeId
is an enumeration with the following values:
InDevelopment
DirectSound
MME
ASIO
SoundManager
CoreAudio
OSS
ALSA
AL
BeOS
WDMKS
JACK
WASAPI
AudioScienceHPI
It is unlikely that more than a couple of these will actually be available on any given system.
method host-api
method host-api(HostApiTypeId $type --> HostApiInfo )
Given a HostApiTypeId
as described above this will return a Audio::PortAudio::HostApiInfo object that describes the host api on this system.
method default-output-device
method default-output-device( --> DeviceInfo )
This returns a Audio::PortAudio::DeviceInfo object that describes the device that will be used for output if the default output stream is asked for. Depending on the configuration of your system this may differ from the default-input-device
. An exception will be thrown if there was a problem determining the device.
method default-input-device
method default-input-device( --> DeviceInfo )
This returns a Audio::PortAudio::DeviceInfo object that describes the device that be used for input if the default input stream is asked for. Depending on the configuration of your system this may differ from the default-outpur-device
. An exception will be thrown if there was a problem determining the device.
method open-default-stream
method open-default-stream(Int $input = 0, Int $output = 2, StreamFormat $format = StreamFormat::Float32, Int $sample-rate = 44100, Int $frames-per-buffer = 256 --> Stream )
This opens a stream for reading and/or writing on the default device, returning a Audio::PortAudio::Stream object or throwing an exception if there was a problem opening the stream.
The default values will almost certainly not work for a lot of applications, of particular importance is format
which should be a member of the enumeration Audio::PortAudio::StreamFormat:
Float32
Int32
Int24
Int16
Int8
UInt8
CustomFormat
NonInterleaved
Which should firstly match the capabilities of your device and also must
match the bit-size and type of the data that is written, if this is not adhered to then you are likely to see segfaults or other memory violation errors. The CustomFormat
is unlikely to be used unless you have created your own portaudio backend. The NonInterleaved
value can be ORed (+|
) with another value to tell the stream to expect the data as separate arrays of data for each channel rather than "interleaved" (samples for each channel in a "frame" appear consecutively in the data,) this may be more convenient for some applications especially with a high channel count.
The $buffer-size
may also be critical for some backends (such as "JACK",) that require this to match the value configured in the backend and that all reads and writes are of the same size, you will need to consult the configuration of the backend to determine what this value should be. Some backends (such as ALSA,) on the other hand, don't seem to be quite as sensitive and this can be set to some value that your application can easily handle in the time available ( that is (buffer-size * channels)/samplerate seconds.) Supplying a value of 0 for buffer size will cause portaudio to adjust its own buffer to suit the calculated latency and throughput, thus allowing for reads and writes based on the available data, however this does not over-ride the earlier comments about fixed buffer sizes for some backends (it really doesn't work well with JACK for instance.)
The $samplerate
should match the capabilities or configuration of your device and the samplerate of the data that is being written, if you pass data to write
which does not match this it is unlikely to playback correctly (you may be able to use Audio::Convert::Samplerate to adjust this in your application.)
The $input
and $output
parameters indicate the number of channels to be opened for input and output respectively, 0 indicating that the stream will not be opened for either reading or writing. The value should match the number of channels present in the device (or a smaller value,) some devices may indicate a larger number of channels than are actually used or connected, if a smaller number of channels is requested than the device presents then they will be taken in the order that the backend supports them, there is no way of specifying a specific range of channels, though you could use the NonInterleaved option to format and ignore the channels you aren't interested in. The implication for backends that are explicitly designed for multi channel applications (such as JACK) is that you may not be able to distinguish named individual channels within the device. Sorry about that, but it appears to be a portaudio limitation.
method open-stream
method open-stream(StreamParameters $in-params, StreamParameters $out-params, Int $sample-rate = 44100, Int $frames-per-buffer = 256 --> Stream )
This returns the Audio::PortAudio::Stream opened with the parameters supplied in the Audio::PortAudio::StreamParameters $in-params
and $out-params
, described below, if either input or output is not required then a StreamParameters type object can be passed for either parameter. $sample-rate
and $frames-per-buffer
is the same as for open-default-stream
and apply to both input and output.
This will throw an exception if the stream cannot be opened with the parameters supplied, you can validate the parameters prior to making this call with is-format-supported
described below.
method is-format-supported
method is-format-supported(StreamParameters $input, StreamParameters $output, Int $sample-rate --> Bool )
This returns a Boolean to indicate whether the Audio::PortAudio::StreamParameters $input
and $output
and the sample rate would work for open-stream
, if it returns False it is likely that open-stream
would throw an exception with the same parameters.
method error-text
method error-text(Int $error-code --> Str )
This returns the appropriate error text for the integer code returned by the pulseaudio API calls, a negative number indicating that it is an error response. As most of the methods that call the portaudio API will infact throw an exception with the appropriate message this may not be all that useful.
Audio::PortAudio::DeviceInfo
This is the class that describes the portaudio devices as discoverd by devices
or device-info
. It has the following members:
struct-version
This is the version of this API structure used, it probably isn't all that useful unless you somehow have mixed versions of backend and portaudio and things aren't working correctly.
name;
This is the name of the device that will commonly be displayed in a user interface.
api-version;
This is the host-api-index
(not the host api type,) of the backend API that supports the device. See the host-api
method to get details of the actual host API being used.
max-input-channels;
This is the maximum number of input channels that the device reports, it should be noted that not all of the number may be usable or connected to a physical device as some drivers may, for instance, examine the underlying hardware to get this value and some of the potential inputs may not be physically connected (laptops with generic audio chips are an example of this,) however it is probably okay to request up to this number of channels, they just may not work.
max-output-channels
The maximum output channels that the device reports. The same caveats apply as for max-input-channels
.
default-low-input-latency
This is a suggested value for input latency for low latency applications, expressed as a Num fraction of a second. This can be used in a StreamParameters
. Some backends such as "JACK" may report this (and the other latency values below,) as being 0 for "client" applications, in which case it might be necessary to use the appropriate value for the "default device" of the host API.
default-low-output-latency
This is a suggested value for output latency for low latency applications, expressed as a Num fraction of a second. This can be used in a StreamParameters
.
default-high-input-latency;
This is a suggested value for input latency for high latency applications, expressed as a Num fraction of a second. This is possibly a better value for Raku applications which are likely to be "high latency".
default-high-output-latency
This is a suggested value for output latency for high latency applications, expressed as a Num fraction of a second. This is possibly a better value for Raku applications which are likely to be "high latency".
default-sample-rate;
This is the default sample rate for the device and will probably be the one that you want to use when you are opening a stream on the device unless you definitely know that the device can adjust itself appropriately. The usual caveats about using the correct sample rate for input and output data apply. Portaudio expresses this as a floating point number ( a Num,) but will almost without exception be an Int. The open-stream
and open-default-stream
take sample-rate
as an Int which will be coerced appropriately.
method host-api
method host-api( --> HostApiInfo )
This returns the Audio::PortAudio::HostApiInfo object representing the host api of this device, which contains useful information about the backed API.
Audio::PortAudio::HostApiInfo
This class represents the details of a host API or "backend" as returned by several methods described above.
struct-version
This is a version number that is internal to the portaudio implementation.
type
This is the HostApiTypeId and the value will be one of the enumeration described above.
name
This is the name of the host API, such as "ALSA", "JACK" etc.
device-count
This is the number of devices that are recorded for this host API and could be used to enumerate the devices.
default-input-device
This is the "device index" of the default input device for this backend, this can be used to get, for example, the default latency for a backend such as JACK that might not report the correct value for its clients, by passing this value to the device-info
method of the Audio::PortAudio object to get the appropriate Audio::PortAudio::DeviceInfo object.
default-output-device
This is the "device index" of the default output device for this backend, this can be used to get, for example, the default latency for a backend such as JACK that might not report the correct value for its clients, by passing this value to the device-info
method of the Audio::PortAudio object to get the appropriate Audio::PortAudio::DeviceInfo object.
This will usually the same as the input device but this shouldn't be relied on.
Audio::PortAudio::StreamParameters
Objects of this class are passed as the $in-params
and $out-params
of the method open-stream
and describe the details required of the input or outputs to be created. All of the attributes must be filled in unless otherwise noted. The validity or otherwise of a particular set of parameters can be checked with `` before actually using them to open a stream.
device
This is the "device index" of the required device (that is 0 .. device-count -1) The device should support the other parameters provided.
channel-count
This is the integer number of channels that should be opened on the stream for the input or output, it should be equal to or less than the maximum number of channels supported by the device.
sample-format
This is the integer value of an item of the enumeration SampleFormat
described above, it should agree with the type of the data that you are intending to read from or write to the stream (otherwise unexpected results may ensue,) not all devices or backend APIs will support all format types and you may have to consult the documentation or configuration. The most commonly supported types are Float32
and Int16
.
suggested-latency
This is the suggested latency for this stream as a Num fraction of a second. You probably want to used the device's or though some backends may entirely ignore this if they have a fixed latency value.
host-api-specific-streaminfo
This is a Pointer to some data that may be supplied to the backend, it is not required by portaudio and should generally be left uninitialised.
Audio::PortAudio::Stream
Objects of this type are returned by open-default-stream
and open-stream
and are the primary interface for reading and writing data from/to a device and for getting the status of the audio stream.
method start
method start( --> Bool )
This starts the stream, making it available for reading or writing. Attempting to read or write without first calling start will give rise to an exception.
An exception will be thrown if the stream cannot be started.
method close
method close( --> Bool )
This closes the stream, releasing the device. If any reads or writes are attempted after it has been closed then an exception will be thrown. You should always close a stream after you are done with it as the API will continue attempting to populate the underlying buffers until closed.
If you attempt to close any already closed stream an exception will be thrown.
method stopped
method stopped( --> Bool )
This returns a Bool to indicate whether the stream is running, it will return True before start
has been called and after close
.
method active
method active( --> Bool )
This returns a Bool to indicate whether the stream is running, it will return True after start
has been called and before close
.
method write-available
method write-available( --> Int )
This returns the number of frames that can be written to the stream without the write
call blocking, It may throw an exception if the stream isn't started or there was some other problem with the stream.
method write
method write(CArray $buf, Int $frames --> Int )
This writes the data in the provided CArray, which must contain data of the correct type as per the StreamFormat
(e.g. num32, int16 etc,) and should contain $frames
* channels
elements.
The data must be written with a frequency such that the underlying buffer is kept filled, an exception will be thrown if the data isn't being fed fast enough and a "buffer under run" occurs before the next write.
For some backends which require a fixed buffer size the number of frames provided should match the buffer size provided when opening the stream, not doing so may result in less than ideal results.
If the stream isn't started or there is some other problem writing the data then an exception will be thrown.
It is deliberate that there is no sugared multi candidates for providing higher level Raku arrays with the data, as the overhead in copying the data will typically eat up too much time to keep the buffer filled. Hopefully this will change in the future.
method read-available
method read-available( --> Int )
This returns the number of frames available to be read from a stream, it may return 0 if the stream isn't opened for input. It may throw an exception if there was some problem with the stream.
method read
method read(Int $frames, Int $num-channels, Mu:U $type --> CArray )
This reads the specified number of frames of $num-channels
from a stream that is opened for input, the number of channels should agree with that provided when opening the stream, and the $type
should correspond to the type of the data that will be used for the StreamFormat
(i.e. num32, int16, etc,). The CArray returned will be typed accordingly and will contain $frames
* $num-channels
elements.
If you do not read from the stream fast enough you may experience buffer over-runs, choppy input, or other artefacts, some backends may consider it fatal and an exception may be raised or the stream closed.
Usually you should be prepared to read the number of frames that were specified when the stream was opened, if the backend requires a specific number of frames for the buffer then this should always be used when reading from the stream.
If there is a problem reading, or the stream has been closed then an exception will be thrown.
method info
method info( --> StreamInfo )
This provides accurate information from the backend while the stream is actually running, it can be used for informational purposes or possibly to tune some computation, the Audio::PortAudio::StreamInfo class is described below.
Audio::PortAudio::StreamInfo
This is the class that is returned by the info
method of the Stream. It contains information from the backend while the stream is running.
struct-version
This is the internal version of the structure returned by the API.
input-latency
This is the calculated latency estimate for input from the backend, it may differ from that provided as the suggested latency when the stream was opened. If the stream was opened for output only this will be 0e0.
output-latency
This is the calculated latency estimate for output from the backend, it may differ from that provided as the suggested latency when the stream was opened. If the stream was opened for input only this will be 0e0.
sample-rate
This is the actual sample-rate that is being used by portaudio, this may differ from that provided to the stream open or reported in the device info if portaudio is aware of inaccuracies in the information determined from the hardware or configration and has adjusted it.