94 6. REAL-TIME FILTERING
static void insertNewSample(short inSample) {
int i;
// Update array samples
for(i=0;i<N-1;i++){
samples[i] = samples[i+1];
}
samples[N-1] = inSample;
}
Here, as a new sample comes in, each of the samples is moved into the previous location in the
array. As a result, the oldest sample sample[0] is discarded, and the newest sample is put into
the array location samples[N-1] .
Now that the N most current samples are in the array, the filtering operation may get
started. All that needs to be done, according to the difference equation, is to multiply each
sample by the corresponding coefficient and sum the products. is is achieved by the following
code.
static short processFIRFilter(short inSample) {
int i, result = 0;
// Update array samples
for( i = 0; i < N; i++ ) {
samples[i] = samples[i+1];
}
samples[N-1] = inSample;
// Filtering
for( i = 0; i < N; i++ ) {
result += (samples[i] * coefficients[i]) ) << 1;
}
return ( result >> 16);
}
is approach adds some overhead to the computation because of the memory changes. Memory
accesses are very expensive in terms of processing time, and having many such accesses is not
computationally efficient for implementation on ARM processors. It should be noted that the
proper way of doing this type of filtering is by using circular buffering. is approach is discussed
in the next section.
6.2. CIRCULAR BUFFERING 95
6.2 CIRCULAR BUFFERING
In many signal processing algorithms, including filtering, adaptive filtering, and spectral analysis,
one requires to shift data or update samples, or to deal with a moving window. For example, a Fi-
nite Impulse Response (FIR) filter basically consists of a list of coefficients which are multiplied
with current and past sampled input signal values. FIR filtering does not involve dependency
on previous output values. For a filter with N coefficients, the output y.n/ based on the input
signal x.n/ gets computed via an FIR filter, that is y.n/ D
P
N 1
kD0
B
k
x.n k/. is equation
can be realized by using a circular buffer where a moving window of the past N 1 sampled
values and the current sampled value is established to compute the output corresponding to the
current input sample.
e direct method of shifting data is normally not efficient and uses many cycles. Circular
buffering is an addressing mode by which a moving-window effect can be created without the
overhead associated with data shifting. In a circular buffer, if a pointer pointing to the last ele-
ment of the buffer is incremented, it is automatically wrapped around and pointed back to the
first element of the buffer. is provides an easy mechanism to exclude the oldest sample while
including the newest sample, creating a moving-window effect as illustrated in Figure 6.1.
Some processors such as DSP processor have specialized hardware for circular buffering.
However, on ARM processors, such hardware is not available. Here let us consider a C imple-
mentation of a circular buffer which could be used on an ARM processor. First, let us establish
the data structure of a circular buffer:
typedef struct CircularBuffer {
short* buff;
int writeCount;
int bitMask;
} CircularBuffer;
In the CircularBuffer structure, buff contains the data and writeCount stores the num-
ber of times the circular buffer has been written to. e rest of the code for the circular buffer
appears below.
CircularBuffer* newCircularBuffer(int size) {
CircularBuffer* newCircularBuffer =
(CircularBuffer*)malloc(sizeof(CircularBuffer));
int pow2Size = 0x01;
while (pow2Size < size) {
pow2Size = pow2Size << 1;
}
96 6. REAL-TIME FILTERING
x(n-(N-1))
x(n-(N-2))
.
.
.
x(n-1)
x(n)
A4
....
n-(N-1) n-(N-2)
n-1 n
x(n-(N-1))
x(n-(N-2))
.
.
.
x(n-1)
x(n)
A4
x(n+1)
x(n-(N-2))
.
.
.
x(n-1)
x(n)
A4
x(n-(N-2))
x(n-(N-3))
.
.
.
x(n)
x(n+1)
....
n-(N-1) n-(N-2) n n+1
A4
Circular buffer of size N
Window of size N
Equivalent
Moving Window Effect
Read newest sample over
oldest sample
oldest
sample
newest
sample
Circular buffer after
N + 1 advancements
of the pointer
Figure 6.1: Moving-window effect.
newCircularBuffer->writeCount = 0;
newCircularBuffer->bitMask = pow2Size-1;
newCircularBuffer->buff = (short*)calloc(pow2Size,sizeof(short));
return newCircularBuffer;
}
void writeCircularBuffer(CircularBuffer* buffer, short value) {
//buff[writeCount % pow2Size] = value;
//writeCount = writeCount + 1;
buffer->buff[(buffer->writeCount++) & buffer->bitMask] = value;
}
short readCircularBuffer(CircularBuffer* buffer, int index) {
6.2. CIRCULAR BUFFERING 97
//return buff[(writeCount - ( index + 1 )) % pow2Size]
return buffer->buff[(buffer->writeCount +
(~index)) & buffer->bitMask];
}
void destroyCircularBuffer(CircularBuffer** buffer) {
free((*buffer)->buff);
(*buffer)->buff = NULL;
free(*buffer);
*buffer = NULL;
}
Since there is not a specialized hardware for computing the current address in circular buffers
on ARM processors, alternative methods typically involve the modulus operation. e use of
the bitMask field is important since a special case of the modulus operation is used here where
the size of the circular buffer is a power of two. e modulus operation inherently involves
the division operation which is typically a slow instruction to execute. Instead, a bit mask is
stored. When the index is bitwise ANDed with the bit mask, only the lower bits of the index
remain—effectively the same result that a modulo operation would generate.
e methods to interface with the CircularBuffer structure consist of
newCircularBuffer which initializes the memory locations and the corresponding
destroyCircularBuffer which releases the memory locations. e writeCircularBuffer
method inserts the sample given by value into buff and increments the writeCount .
Lastly, readCircularBuffer returns the value at index , where index zero corresponds to
the newest sample inserted into buff and increasing values corresponding to samples further
in the past. An implementation of this circular buffer is shown in the following code.
static short processFIRFilter(short inSample) {
writeCircularBuffer(inputBuffer, inSample);
int i, result = 0;
for( i = 0; i < N; i++) {
result += (readCircularBuffer(inputBuffer, i) *
coefficients[i]) << 1;
}
return (result >> 16);
}
Although the performance of this method may appear acceptable, it is not the best that can be
achieved on ARM processors. To reduce the computational burden of creating a moving window
of input samples, an alternative approach known as frame processing is discussed next.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.159.224