Home
ALab Developer Tutorial
The very first thing to know about ALab is, that every object that can produce sound
must be a subclass of AObject. The way
an AObject
provides its sound data differs from most other concepts.
Usually a method calling for sound data would be coded like
getValueAt(channel, position)
. This method would calculate the value at
position
and at audio channel channel
. In ALab it is only
possible to ask for the next value (method
nextValue(channel, rate)).
Depending on the samplerate the next value is calculated.
To ensure correct working of ALab it is neccessary to ask for values in a certain
sequence. The lowest channel has to be called first, followed by higher channels and
ending with the highest one. There are two abstract methods that must be implemented
by a subclass of AObject
:
getValue(channel, rate)
calculates the next value. copy()
returns a copy of itself that returns exactly the same values and uses copies of the
AObject
s it uses (in fact, the copy method doesn't change much. To
implement the copy method it is neccessary to build a constructor that can copy a
class of its type. The copy method simply calls this constructor with itself).
ASilence
,
Diagram
(all example java-files can be found in docs/tutorial/examples)
In this tutorial we will create new ALab modules, subclasses of AObject
.
The first module created silence the next module is an impuls generator. This
oscillator will be a subclass of AWave that is
a subclass of AObject and provides typical
oscillator methods. Every AWave
object has a certain frequency and phase.
To calculate the next value we only need to know the current phase at each channel.
This phase is calculated by the method
nextPhase(channel, rate)
and returns a value between -PI
and PI
. A new circle starts
at 0
. But it is also possible that the last phase was -0.01
and the current phase is 0.24
, so the new circle doesn't start with zero.
It is also possible that the phase is running backwards (negative frequency). To
know, wheather a new circle started or not, we can use the method
isZero(channel). This method
returns true
if a new circle started. Our impuls generator will return
1
every new circle and -1
if the phase changes from
PI
to -PI
. To know when the phase changes, we remember the
last phase and return -1
whenever the last and the current phase are
different in sign and the method isZero()
returns false
.
Phase Diagram
,
AImpuls
,
Flow Diagram
The next module will not create sound, but change the values of any
AObject
. At first we simply take and store the given object and return
its values if asked for any value. To do so, we have to build a constructor that uses
an AObject
as parameter. This AObject
is copied and stored.
We have to copy it, to ensure that this object is not used by any other object. If it
would be used by another object the result would not be correct, because it is not
possible in ALab to ask for any value two times. If we ask any AObject
for values two times, the return values will be different, because always the next
value is calculated.
AUseObject
Our module will be able to be modulated. Modulation always works this way in ALab:
There is always one certain value (pan
in our next example) that must
be declared in the constructor. This value is of type double
and has no
limit in range (but can be limited to ensure correct working). Most modules work with
such values in the range -1..1
. There is only one value for all channels,
but this value can be changed (modulated) for every channel (so it is possible to get
different values for each channel). To modulate such a value, a modulator for this
value must be defined (in our next example it is called panMod
). Any
AObject
can be used as modulator and the values of this modulator are
multiplied with the value to be modulated (nextObjValue()). If such a value is
5
(for all channels) and the modulator is 1
at channel zero
(left) and -1
at channel one (right) than the value will be used as
5 (5x1=5)
on left channel and -5 (5x-1=-5)
at right channel.
The range of the modulated values is between -value
and value
.
AUseMod
To understand our module we will look at a certain problem before: We have three
sounds, a cow saying 'MUH', a chicken saying 'GACK-GACK' and a pig saying 'GRUNZ'.
We have each of this sounds as one channel (mono) AObject
s. We want
this three sounds to be placed into a stereo (two channel) AObject
.
'MUH' left, 'GACK-GACK' middle and 'GRUNZ' right in the stereo panorama. Our module
APan
will be able to do this. APan
is always a stereo object
and uses any AObject as input. The value
pan
tells the position in the stereo panorama. If the input
AObject
has n
channels, the pan value modulator must also
have n
channels to take effect on all channels. In our example the pan
modulator will use -1
for 'MUH', zero for 'GACK-GACK' and 1
for 'GRUNZ'. If the pan modulator has less channels, the pan value will be zero for
these missing channels.
APan
,
APan(without docu)
,
panorama
This is the end of the developer tutorial. If you did understand everything you
will be able to build interesting new modules alone now. Try to avoid constructors
that throw exceptions and cast all exceptions to AException
. If you
want to create complexer modules, you will probably need more informations. You
can get these informations by studying the source code of ALab or you can send
a mail to the support address.
Home