Detailed
description: software
Get the sources
here: the LKM source Drum1.c, communication
program Synticka.py and startup
script SetupEnviron.sh.
In the Linux kernel, there is a system that
detects if a CPU is unresponsive, and in this
case the system will note that the processor
running the drum application is no longer
available. So it will sound the alarm. This must
be disabled with the following shell command
(run as root):
#
echo 1 >
/sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
Also, interrupts are disabled as much as
possible. When audio signal is generated, the
interrupts are kept disabled in the LKM
(however, ARM fast interrupts, FIQs, are always
enabled). When no signal is generated,
interrupts are enabled again.
The LKM is coded in C. It consists of the
following main parts:
- An array of pointers to sound samples,
called Signals. The pointers
in the array are structs and also hold other
information, such as sound amplitude, and an
index pointing to the current audio
frame in the sound sample. Each audio
frame consists of two signed 16 bit integers
(two:s complement, one for left, one for
right).
- The first part of the table is used to
hold pointers to currently active sound
samples (samples that are used to generate
sounds). At the present, 16
simultaneous stereo sounds are
supported. The rest of
the table contains pointers to all sound
samples (active and inactive; this latter
part of the table was not mentioned in the
simplified algorithm above).
- Each drum pad is associated with a
parameter block of the type ADCparamType.
These parameters are mostly related to the
drum pad signal processing, but there is
also an integer with the name SoundIndex.
This index, which is used to index the Signals
table, associates a drum pad with a sound
sample that is played when the pad is hit.
- When a pad is hit, the SoundIndex
variable us used to fetch the pointer to the
sound sample to be played. This pointer is
then inserted into the first part of the Signals
table, where pointers to active sounds are
stored.
- The main loop that generates sound and
reacts to drumpad hits is situated in the DrumLoop
routine. From this loop, the routine MixAndOut,
which generates sound, is called several
times. The loop is also calling ReadADCchannels
(once per loop iteration), which will check
an ADC channel for a drum pad hit, and, if a
hit is detected, set up a pointer to the
activated sound sample in the first part of
the Signals table.
- MixAndOut looks for pointers to
active sounds in the first part of Signals.
It will mix (add) all frames (multiplied
with the corresponding hit ampilude) for the
current point in time from all active sound
samples. The result is then written to the
DACs. The indices pointing at the current audio
frame for each active sound sample is
incremented. If the end of the sound sample
is reached, then the sound is deactivated
(amplitude set to zero).
- ReadADCchannels is the most
complicated routine in this group. It reads
the ADC channels (one channel per loop
iteration) and stores the result in a ring
buffer (one ring buffer per channel). It
then performs signal processing on the data
in the ring buffer: the derivative of the
signal is calculated, then squared and
digitally low pass filtered. If this
processed signal exceeds a set trig level,
the sound sample associated with the drum
pad is inserted into the first part of Signals
(for active sounds). A flag Holdoff
is also set. For subsequent reads from the
same ADC channel Holdoff will be
set, and the sound sample associated with
the pad will not be activated again.
Instead, the maximum amplitude of the hit
seen so far is stored away, to be used for
amplitude modulation of the pad sound. Only
when the hit amplitude has fallen
sufficiently below the trig level will the Holdoff
flag be reset, and the drum pad will be
ready for another hit. Observe that this is
of course independent of the sound
generation, so the sound generated by the
hit can be active long after the drum pad is
ready for next hit, making polyphony (from
one pad) possible.
- The 88.2 kHz clock is generated by the
Raspberry Pi GPCLK0. To synchronize sound
generation to the 88.2 kHz clock, the
software will wait (active polling) for a
high-to-low clock transient before updating
the set of four DAC channels.
As mentioned above, the LKM is controlled from a
python module
(Synticka.py),
communicating trough the
/proc
filesystem. The LKM creates a directory,
drum,
under
proc, and three files in the
drum
directory:
Command, ADC and
DAC.
Command is used to interact with the LKM,
while
DAC and
ADC are used
mostly for debugging.
How to
use Synticka.py to control the LKM, Drum1
To start the software, type
#
SetupEnviron.sh
in a shell, as root. The script will start the
KLM,
Drum1.ko, so it will not
return. In another shell, as a normal user,
type:
$
python -i Synticka.py
This will give a python prompt with all the
interface routines loaded. In the following, the
usage of the most important routines will be
described:
MakeSig(SoundIndex,
LeftChannel, RightChannel, Amplitude=65535)
Makes a sound sample (also allocates memory for
it) from one-dimensional numeric arrays
(lists)
LeftChannel and
RightChannel, and inserts a pointer to the
sound sample into the
Signals table at a
position indicated by
SoundIndex.
LeftChannel and
RightChannel
should have the same length. Max. value for
Amplitude
is 65535 (0xFFFF, default), minimum is 0. If
Amplitude
is
zero, the sound will be inactive
(disabled).
ReadSig(SoundIndex,
SigDataIndex=0, SigDataIndexEnd=2000000000,
ChIndex=-1)
Reads a sound sample pointed at by a
Signals
table element at
SoundIndex. Default,
the whole sample is read, unless
SigDataIndex is used to give a starting
index, and/or
SigDataIndexEnd is
used to give a stop index. The result is
delivered as a list of tuples, where the first
element och each tuple is the left channel and
the second tuple is the right channel. If
ChIndex
is given as 0, only the left channel will be
delivered as a list if integers, or, if
ChIndex
is given as 1, the right channel.
InitSig(SoundIndex,
Size, Amplitude=65535)
This will make a sound sample consisting of
zeros, and set a pointer to the sample in the
Signals
table at position indicated by
SoundIndex.
The sample will have size
Size and
amplitude
Amplitude. Max. value for
Amplitude
is 65535 (0xFFFF, default), minimum is 0. If
Amplitude
is
zero, the sound will be inactive
(disabled).
SetSigPars(SoundIndex,
Amplitude=65535)
Will set parameters associated with the sound
pointed at by Signals table element at
SoundIndex. Currently only amplitude can be
set: max. value for
Amplitude is
65535 (0xFFFF, default), minimum is 0. If
Amplitude
is
zero, the sound will be inactive
(disabled).
SetADCpars(Channel,
SigIndex=-1, TrigLevel=10000,
FilterFact=100, FilterDerivativeGap=9)
Sets parameters associated with a drumpad ADC
channel.
Channel should be between 0 and
7,
SigIndex is a Signals table index
indicating a sound sample that is played at
drumpad hits.
TrigLevel
is the hit sensitivity.
FilterFact
and
FilterDerivativeGap
controls the drumpad hit signal processing:
smaller
FilterFact
allows for higher frequencies to pass trough the
low-pass filter, which makes the drum faster,
but less sensitive (= more suspectible to false
triggers).
FilterDerivativeGap
controls the delta when calculating hit signal
derivative (
derivative = HitSignal[i] -
HitSignal[i - FilterDerivativeGap]).
Stop()
This command will cause the process
currently running in the LKM to exit to the
shell. It will not unload the LKM from the
kernel.
ReadAudioFile(Name)
Reads an audio file in Audio Interchange File
Format (.aiff).
Name should be an ASCII
string. It returns a couple of arrays, for the
left and right channel. These array can then be
used with a call to
MakeSig, e.g.:
>>>
DataLeft,
DataRight = ReadAudioFile("AudioFile.aiff")
Audio file
AudioFile.aiff has sample rate:
44100 Hz, and is 147402 *4 bytes
long.
>>>
MakeSig(25,
DataLeft, DataRight)
>>>
SetADCpars(0,
25)
# Associate pad 0 with sound number 25.
Detailed
description: hardware
As mentioned, the hardware consists of the
Raspberry card, with an 8 channel 14 bit ADC and
a 4 channel, 16 bit DAC connected to the
Raspberry SPI 0 bus. The drumpad electret
microphones are connected to the ADC, and the
DAC is used to generate sounds. The
prototype-like hardware is built using a
combination of wire-wrap and soldering.
Schematics: