Pliant audio system
Pliant audio system only works under Linux or FullPliant operating system because it uses Linux kernel drivers to deal with audio boards, and external libraries available in mainstream Linux distribution to handle MP3 (lossy) and FLAC (lossless) compression.
Pliant audio system is using a push through filters mechanism:
Samples pushed from a filter to the next one are encoded as 32 bits floating point values.
The audio filter interface is defined in /pliant/audio/core/prototype.pli
var Link:AudioPrototype p
An audio filter exposes three methods: 'open' for setup, 'write' for providing it samples, and 'close' for ending operations. So, the correct sequence is to call 'open' once, then 'write' several times, and finally 'close' once. 'write' and 'close' should not be called if 'open' returned failure.
method p open channels rate options -> status
No bits per sample is indicated since samples will always be provided as Float32.
method p write samples count -> status
'count' is the number of samples, so assuming that 'channels' parameter passed to 'open' was 2, then the size of buffer pointed by 'samples' is 8*count.
method p close -> status
Now I can explain the all machinery principle.
Two extra methods are provided:
method p pending -> count
Returns the number of audio samples that have already been provided through 'write' methods, but have not been consumes yet. This is usefull to synchronize time on an audio final filter.
method p write16 samples16 count decoder -> status
'write' is expecting samples to be provided as a buffer of 32 bits floatting point value.
As I have just explained it, audio sources are generating the set of audio samples. An audio source function is not returning until all audio samples have been played. So, it has a 'stop' parameter that enables another thread to request it to stop immediately.
A stream (or a file)
This is implemented as function 'audio_play' in module /pliant/audio/format/all.pli
function audio_play stream format output options stop -> status
Supported formats can be 'wave' (uncompressed) or 'flac' or 'mp3'. 'format' parameter can be a file name provided it's extension is matching one the 3 supported formats. If 'format' is empty, 'audio_play' function will (try to) discover it from the file content. 'wav' is accepted as an alias for 'wav'.
An audio board (record sound)
This is implemented as function 'device_play' in module /pliant/audio/core/device.pli"
function device_play output options stop -> status
Maybe this function should be called 'device_record' since it will record from the audio board in the end. Here is a possible value for 'options':
"board 0 channels 4 rate 96000 bits 24 oss alsa volume 10 mic_volume 80"
See 'An audio board (play sound)' paragraph bellow for details about these parameters.
This is implemented in /pliant/audio/filter/tone.pli
The core of it is the shelving filter implementation. A shelving filter is defined by three parameters: the power adjustment bellow the threshold frequency, the threshold frequency, and the power adjustment above the threshold frequency. See Wikipedia.
I have implemented shelving through experimenting and earing, so that in the end, I don't know if what I have implemented is well known (100th time reinvention of the wheel) or new.
Then in the 'AudioTone' type that implements the Pliant general tone adjustment filtering mechanism, I just set an array of shelving filters according to the requested adjustments. Here is a sample containing all possible parameters (weather it makes sense to adjust all of them at once is another story):
"bass 3 bass_hz 200 tone 1 tone_hz 1000 trebble -3 trebble_hz 5000 gain -1"
Please notice that in the program I've misspelled treble as 'trebble', so you have to do the same or your settings will just be ignored. Anyway, I promise: I will correct the spelling as soon as we reach 1000000 users of Pliant audio player :-)
According to my personal taste, best recordings (assuming that you use a high quality audio system both at recording and listening time) are the ones done with only two microphones properly installed (ORTF positioning). Anyway, this recording technique has three drawbacks:
So, the nowadays standard is rather to use one microphone per audio source (instrument, singer), then do mixing to reduce to two tracks. The huge problem is that current mixing techniques are still very naive so that the final sound is often completely unrealistic.
When using speakers, this is not a that serious issue because the speakers will automatically provide some crossfeed and positioning informations, but when using a headset, it can be very disturbing because the brain continuously tries to analyze inconsistent informations.
The general principle of crossfeed (it can be achieved either through hardware or software) is to add the right audio channel to the left one, reduced, delayed and maybe tone adjusted to recreate the information that is always present in real situation so that the brain is desperately searching when earing using headphones a badly mixed recording.
Pliant crossfeed implementation is in /pliant/audio/filter/crossfeed.pli module and can be adjusted trough the following parameters:
"crossfeed -9 crossfeed_delay 250 crossfeed_difference 0.5 crossfeeld_filter_db -6 crossfeed_filter_hz 1000"
'crossfeed' is the number of dB of amplification of the signal that will be copied to the opposite side.
It enables to change sampling frequency and is implemented in /pliant/audio/filter/resample.pli
It can be activated through the 'rate' keyword:
A sample usage could be in 'audio_convert' high level function described at the end of this document.
Enables to extract a subpart of the track, and is implemented in /pliant/audio/filter/select.pli. Start and stop times are defined in seconds:
"from 60 to 180"
Also usefull in 'audio_convert'.
Provides informations about the audio track in a text variable. The variable is intended to be read by another thread, so it's access is protected by a semaphore.
It is activated through providing 'info' option to 'audio_filter' or 'audio_convert' functions described bellow:
"info title[dq]Me playing 'Etoile des neiges' on the accordion[dq]"
Here is a sample value that the application might get while the track is playing:
"title[dq]Me playing 'Etoile des neiges' on the accordion[dq] elapsed 12.3 maxi 0.2 clip maxi0 -0.3 maxi1 0.2 forever_maxi 2.1"
'elapsed' is the time from the track begin, in seconds,
The 'AudioWhat' filter implemented in /pliant/audio/filter/what.pli will display on console informations about the audio track, such as number of channels, sampling rate, and sometime bits per sample. Will be modified to use Pliant trace mechanism.
All at once
In module /pliant/audio/filter/all.pli, a 'audio_filter' function is provided enabling to set all filters at once through the single 'effect' parameter:
function audio_filter base effect report sem -> final
'report' and 'sem' are used to provide feedback from the optional 'Info' filter.
Audio final filters
A stream (or a file)
This is implemented as function 'audio_output' in module /pliant/audio/format/all.pli
function audio_output stream format -> output
Supported values for 'format' parameters are 'wav', 'wave', 'flac' and 'mp3'.
When the 'open' method of the 'AudioPrototype' object returned by 'audio_output' will called, some options will be used to configure compression.
For FLAC compression:
"best bits 24"
'best' means try to achieve best possible FLAC compression ratio, at the expense of a lot of computing power consumed, so slow compression speed.
For MP3 compression:
"kbps 192 hifi best fast very_fast"
Don't set all parameters as in this example since they are conflicting !
An audio board (play sound)
This is implemented as type AudioDevice in module /pliant/audio/core/device.pli
Since it implements the filter API, the audio device is configured through the 'options' parameter it receives when it's 'open' function is called. Possible options are:
"oss alsa board 3 bits 24 volume 75"
'oss' forces to use Linux OSS as opposed to Alsa,
A full sample
The following code is a simplified version of what you can find in the main function of the Pliant audio player (function 'audio_play' in module /pliant/appli/audio.pli):
An audio player is provided in /pliant/appli/audio.ui (accessed through 'Application' 'Audio' from Pliant main menu).
It's user interface is rough, but various filters (tone adjustment, crossfeed) implemented in Pliant audio system can be used and easily adjusted. So, the audio quality of this player, from an audiophile point of view, mostly depends on the quality of the Pliant shelving filter and crossfeed compared to mainstream implementations. Please feel free to review, test and comment !
Audio files have to be stored in file:/audio/ directory. I really have to make this parametric :-(
Module /pliant/audio/appli/convert.pli provides a high level 'audio_convert' function:
var ExtendedStatus s := audio_convert "file:/tmp/tracks/" "file:/tmp/tracks/" "format [dq]mp3[dq] replace"
'replace' means that the original files will be removed after re-encoding.