Author: Andrei Borovsky, audiocomps@mail.ru
This document covers the following topics:
Introduction to ACS Programming
What other libraries you need to use all the features of ACS
What's new in ACS 2.2
There are also some other minor changes and fixes in ACS.
Thanks Section
Several people have made contributions to the ACS code. Among those I want to thank specially:
Thomas la Cour, tlc@top-house.dk, http://www.top-house.dk/~nr161/delphi/ - for providing Monkey Audio input/output components.
Jan Henrik Ejme (jan.h.ejme@xhlp.com) - for adding ACM-plaing support.
Thomas Grelle (grelle@online.de) - for improving CDRip support.
Jan Bartels (ATR Industrie-Elektronik GmbH & Co. KG, Viersen, Germany) - for some useful corrections and suggestions.
And all other folks who sent me their comments and suggestions.
Introduction to ACS Programming
The Audio Components Suite is a set of components designed to handle different sound processing tasks, such as reading and storing data in different audio formats, working with sound hardware, audio streams mixing and so on.
Most of the components in the Audio Component Suite belong to one of three categories: input components, output components, converter components. There are also two components that do not fit into this scheme. These are TCDPlayer component and TMixer component.
The input components acquire audio input from some source (device, disk file, etc.) and convert it into a digital audio stream which is the internal audio representation in ACS. The output components take in the digital audio produced by input components and send it to some audio output device or store it on disk in a specified audio file format. Converter components perform different operations on audio streams. These components take in a digital audio stream and output a stream which is modified according to the function of the component. The following diagram illustrates the flow of audio data in the ACS processing chain:
All components are linked via their Input properties. Red arrows show the direction in which the data flows in the chain. In this example we read audio data from the soundcard input with TAudioIn (input component), pass it through the TSoundIndicator component ("converter" component) and store in a *.wav file via TWaveOut component (output component).
The converter component is not a necessary element in this chain. In many cases you will just connect input and output components together to convert data from one format to the other or to perform I/O with some particular device. As you can see ACS isn't just a collection of components implementing interfaces to some hardware or file formats. It is rather a set of building blocks with which you can construct a complete sound-processing application.
The driving force of this sound processing chain is an output component. Output components use internal thread objects that call data-retrieving functions of the input/converter components attached to the output component. Note that ACS provides you with several ways to set the amount of CPU time consumed by these threads: TheradPriority, Delay, and SuspendWhenIdle properties. You can use this features to set the best performance/CPU usage ratio in your ACS applications.
Suppose you want your application to convert data from audio CD tracks to Ogg Vorbis audio compression format. First, you put into your form a CDIn input component that reads data from CD tracks (don't confuse this component with TCDPlayer component which is designed to play CD tracks). Now you need an output component, and in our case it is VorbisOut. In order to chain two components together you have to assign CDIn component to the VorbisOut's Input property:
VorbisOut1.Input := CDIn1;
Now set the CD track you want to be converted:
CDIn1.StartTrack := 1;
CDIn1.EndTrack := 1;
This tells the component to read only one track (the first track on the CD).
Now you have to set up a file name for an output file:
VorbisOut1.FileName := 'Track1.ogg';
And desired compression ratio:
VorbisOut1.Compression := 0.1;
Believe it or not, you have now set up everything for the data conversion. In order to perform the conversion, simply call:
VorbisOut1.Run;
The actual compression process runs in its own thread, while your main thread can do something else. You can control how conversion goes on by checking VorbisOut1's Progress property. When the conversion is finished, the VorbisOut1.OnDone property will be called.
You get access to an input component's data directly in a non-threaded way. The following example shows how data can be obtained from an audio CD with CDIn component.
var Len, DataSize : Integer; Data : Pointer; begin
...
CDIn1.StartTrack := 1;
CDIn1.EndTrack := 1;
CDIn1.Init;
Len := CDIn1.GetData(Data, DataSize);
while Len <> 0 do
begin
// Process CD data
Len := CDIn1.GetData(Data, DataSize);
end;
CDIn1.Flush;
Let's take another example. Suppose, we want to get stereo audio data from a *.wav file, convert it to mono and save to another *.wav file. We will need WaveIn component, which reads data from a *.wav file, MSConverter component that converts stereo strem to mono and vice versa, and WaveOut component that writes data to a *.wav file. Now we chain these three components:
WaveIn1.FileName := 'input.wav';
MSConverter1.Input := WaveIn1;
WaveOut1.Input := MSConverter1;
WaveOut1.FileName := 'output.wav';
WaveOut1.Run;
Converter components take input from input components or from other converters and output data to output components or other converters. It is possible to chain any number of converters this way. There is a special converter in ACS called AudioMixer. This component takes in two audio streams and mixes them into a single output stream. In the new version of ACS AudioMixer can also concatenate to incoming audio streams, i.e. make one stream where two input streams go consecutively
Note: the technique described below is only useful when the input sources have different audio parameters (sample rate, number of channels, number of bits per sample). In case when all the inputs have the same audio parameters you may want to use TInputList component instead.
In some cases you may want to start the next audio task automatically right after the previous task has been finished. Suppose you build an audio player with a kind of playlist. You want the files in the playlist to be played one after another.
When the current file playback or other output operation is finished output component generates OnDone event indicating it has finished current output operation. The problem is however that generally you cannot call output component’s Run method from the component’s own OnDone event handler to play the next file. You could use messages or timers to bypass this problem but there is another, purely ACS way to do it.
Use two output components instead of one. In our example you can use two AudioOut components in your player application. When you start playing the playlist the first AudioOut component takes the job. After the first audio file playback is finished the first AudioOut component generates the OnDone event. In this OnDone event’s handler you select the second file from the playlist and make the second AudioOut component to play the file by calling its Run method. When the second AudioOut component has finished the playback it also generates the OnDone event. In this OnDone event’s handler you turn back to the first AudioOut component and make it to play the third file from the playlist and so on. You can find a working example of this technique in the Audio Player Demo.
This topic is thoroughly described in the farther documentation, but people send me so many questions on it that I decided to put an additional explanation here.
The actual input and output is performed in ACS by threads. It is natural to put the thread into the suspended state when it has no job to do and resume the thread when it has one. However, when developing under Kylix IDE suspending and resuming threads may be inconvenient since every time such an operation is performed, signals are emitted. These signals are trapped by the IDE, and the program execution stops. That is why SuspendWhenIdle property has been introduced. The idea behind this property is that when you develop an application under Kylix, you set this property to False, so that the ACS threads don’t get suspended. When you’re preparing the final release, you set this property to True which makes the threads to be suspended when there is no job for them and thus reduces CPU load.
Under Linux the default value of SuspendWhenIdle property is False so threads don’t get suspended by default. Under Windows, where there is no problem with signals the default value of this property is True and there is no need to change it.
Two following pictures show the CPU load with SuspendWhenIdle set to False and True respectively. The high plateau on the right side of the first picture corresponds to the high CPU cycles consumption by the idle thread not being suspended.

Output components' threads are designed in such a way that when an exception occures within the thread, it is caught by the thread procedure itself and isn't propagated. To be informed about exceptions that might arise during thread execution, assign a handler to the OnThreadException event of the output component. A reference to the exception object is passed to this event handler, so that you can analyse the exception.
Starting from the ACS version 2.2 most of the components that have Input property (output and converter components) allow assigning new input on the fly (i.e. while the component is doing playback). It is important to remember that when changing input on the fly, new input audio stream parameters must be thhe same as the old ones. Note that TAudioProcessor Component doesn't allow modifing Input property while it is playing. InputList component allows modifing all input items at anytime except the item, currently being played. You can switch to other InputList's item on the fly by changing the components CurrentInput property.
This section explains how to avoid some errors that are most often encountered by the programmers beginning to use ACS.
AudioOut1.Stop; while AudioOut1.Status <> tosIdle do;
Under Linux the shared libraries required by ACS (Ogg Vorbis libraries and the like) may have different filenames and locations depending on Linux distro. To simplify libraries linking ACS uses dynamic linking and run-time libraries search. This search however can slow down appliactions sturtup. If you develop an application for a particular distro and you know where exactly some library is placed and what its file name is you may disable run-time search. This requires you to provide full library name and disable SEARCH_LIBS directive in the appropriate unit. Let's for example look at VorbisFile unit. If the run-time search is anabled, the corresponding fragment of the unit looks like this:
{$IFDEF LINUX}
LibvorbisfilePath = 'libvorbisfile.so*'; // search mask
{$DEFINE SEARCH_LIBS} // directive
{$ENDIF}
In order to disable run-time search change this fragment to:
{$IFDEF LINUX}
LibvorbisfilePath = '/usr/lib/libvorbisfile.so.0.1'; // exact file name
// {$DEFINE SEARCH_LIBS} // directive disabled
{$ENDIF}
Some of ACS components require certain shared libraries, which are not included in the package but are publicly available and may be found on many sites. In order to use VorbisIn and VorbisOut components you will need Ogg Vorbis shared libraries. These libraries are available at www.xiph.org (you will need either Windows or Linux versions of these libraries depending on the development environment you use).
Under Linux the TMPEGIn component requires smpeg library by Locki Entertainment. AOLive Linux component requires libao library.
TFLACIn and TFLACOut components depend on libFLAC.dll library under Windows and libFLAC.so library under Linux.
TMP3ToWav component requires MADLib.dll library under Windows and libmad.so library under Linux. Under linux libmad.so library is also required for TWaveIn component to read ACM-encoded files (you don't need this library if you only read wave files in other encodings).
Monkey Audio components require MacDLL.dll library. This library is included into windlls.zip archive (see below). You can also download it with Monkey Audio SDK available on the Net.
Under Windows TCDIn component requires CDRip.dll library distibuted under GPL (I use the variant of the library distributed with NeoAudio). TMP3Out component requires lame_enc.dll (under Windows) or libmp3lame.so (under Linux).
Note on using LAME: LAME is an encoder created for educational purposes only. It may be illegal to use LAME in your Country. Check this out.
It is important to note that since all libraries required by ACS components are loaded dynamically at run-time, you can use ACS as a whole without these libraries. Of course, those components requiring certain library will only work if the library is found.
Windows note: you can download all dlls required by ACS components from http://www.mtu-net.ru/aborovsky/acs/windlls.zip (binary form). You can also download CDRip.dll and lame-enc.dll sources from http://www.mtu-net.ru/aborovsky/acs/CDRip.zip and http://www.mtu-net.ru/aborovsky/acs/lame-3.93.1.zip
.Installation
If you have installed previous version of ACS, uninstall it before installing the new version. Click Component->Install Packages menu item. In the opened dialog box select the Audio Components Suite package and click "Remove" button. Make sure to remove all the previous version files from the IDE search path.
To install Audio Components Suite, first compile the package. Create or select directory where you want the components to be installed (you can use your Delphi/Kylix lib directory). We will call this directory ACS directory. Now go to the Src directory of the ACS distribution, and copy all the files from either Linux or Win32 directory to the ACS directory. Copy all the files from the Common directory into ACS directory. Now go to the ACS directory, open ACS.dpk file in your IDE. Press "Options" button, in the opened dialog go to Directories/Conditionals tab and set up the output paths to the directory where you have copied ACS source files, press OK and then in the package window press "Compile" button. Click Component->Install Packages menu item. In the opened dialog box click "Add" button. Browse to the ACS directory and select the file called bplACS.so.2.2 (Kylix) or ACS.bpl (Delphi). After this two new tabs: "Audio I/O" and "Audio Processing" will appear on the components palette. Unless you have selected your Kylix/Delphi lib directory as ACS directory, you will have to add the ACS installation directory to the Kylix/Delphi libraries search path. This can be done on the "Library" page of the "Environment Options" dialog box (Tools->Environment Options... menu item).
Copyright © 2002, 2003 Andrei Borovsky. All rights reserved.