7.1 Theory
A
S discussed in Chapter 6, using frame-based processing greatly increases the efficiency of DSP programs. The CPU performing the signal processing algorithm is only inter-rupted at the end of a frame rather than at every sample. In this chapter, we will show how frames can be used for time-domain digital filtering similar to the filters we discussed in Chapter 3.Recall that a time-domain implementation of a digital filter involves an iterative calcu-lation of the filter’s difference equation. The only change in this chapter is that we will be dealing with our samples a frame at a time rather than a sample at a time. For simplicity, we will restrict this discussion to FIR filters, but IIR filters can be implemented with frames in essentially the same manner.
7.2 winDSK Demonstration
The winDSK program does not provide an equivalent function; all the filtering in winDSK is accomplished sample by sample to keep the code very simple.
7.3 MATLAB Implementation
Frame-based filtering in MATLAB can be accomplished using resources such as the Data Acquistion Toolbox or by using Simulink, both available from The MathWorks, or by using the MATLAB-to-DSK interface software included with this book. See Section 2.4.1 and Section 2.4.2 for examples, and Appendix E for more details.
7.4 DSK Implementation in C
To demonstrate frame-based digital filtering on the DSK, we provide a C program that implements a filter similar to the FIR filters shown in Chapter 3. Before discussing the C code, however, it’s important to understand the process required to implement an FIR filter in a frame-based manner.
141
142 CHAPTER 7. DIGITAL FILTERS USING FRAMES
Figure 7.1: Implementing a second-order FIR filter with a frame-based approach. Note that the b coefficients can’t “slide” past the edge of Frame 1 without some programming
“tricks.”
7.4.1 Understanding the FIR Process for Frames
Using triple-buffered EDMA-transferred frames for I/O will make the program far more efficient and faster than the sample-based versions discussed in Chapter 3. However, the program will have available to it a fixed-size frame of samples at any given time, which complicates the filter convolution at the “edges” of the frame. Recall from Chapter 3 that the output y[n] of a digital filter is calculated by performing the discrete-time convolution of the input x[n] with the filter’s impulse response h[n]. When written as the difference equation for an FIR filter, the numerator values b[n] are equivalent to the impulse response h[n]. Making this substitution (b for h) and remembering that an FIR filter of order K has K + 1 coefficients, the convolution sum becomes the general form of the FIR difference equation, namely,
y[n] =
K k=0
b[k]x[n− k] for n = 0, 1, 2, . . . , N − 1,
where N represents the length of x[n] and K + 1 is the length of b[n].1 This equation tells us that to calculate the values for an entire frame’s worth of filter output, we will need to “slide” the filter coefficients “across” the entire frame of filter input, multiplying and summing point-by-point as indicated by the equation above. This process is depicted in Figure 7.1, where it can be seen that there is a potential problem at the “edges” of the frame.
There is only a single frame of input data available at any given time. In Figure 7.1, the values in Frame 2 aren’t available yet, and the values from the previous frame shown to
1There is nothing magical about the lettersK, N, M, and so on. Sometimes N is used to represent the length of the input data as we do here; another timeN might represent the order of a filter. The context of the discussion should make clear what the letters represent.
7.4. DSK IMPLEMENTATION IN C 143 the left of Frame 1 would be “gone” by now unless we use some programming “tricks” to keep those values around. If we ignore the “edges” of the frame, we would be ignoring the initial and final conditions of the filter for that frame of data, and we would not correctly implement the filter. For audio applications, the result of ignoring these “edge” problems would be heard as a distinctive “clicking” or “popping” noise in the output occurring at the frame rate.
7.4.2 How to Avoid the “Edge” Problems
How do we fix this problem? We create a buffer large enough to contain both the frame of current input data and also have room to hold the necessary edge values from the previous frame. In Figure 7.1 this would mean an array that includes both the values labeled “From previous frame” and the values labeled Frame 1. We “slide” the filter starting from the left most element (the first “From previous frame” element) until the right edge of the filter reaches the end of Frame 1, indicated in the figure by a thick, dark line. We can’t go any farther than this because the data for Frame 2 isn’t available yet. Before the current frame (Frame 1) is transferred out and the next frame (Frame 2) comes in to overwrite it, we copy the right edge values of Frame 1 into the locations labeled “From previous frame.”
The next frame values are stored in the remaining locations, so we have effectively “saved”
the values from the previous frame. In this way, the edge effects are eliminated, as will be shown in the actual C code.
7.4.3 Explanation of the C Code
As an illustrative example, we will show you a program that implements a simple low-pass FIR filter that provides a similar output result to that of the FIR filters discussed in Chapter 3. But the method used to obtain our output will be to use frame-based processing, written in such a way that we avoid the “edge” problems discussed above.
Thus, the associated “clicking” or “popping” noise in the output is avoided. Before you read the program listing, you may want to glance back at Figure 7.1.
The C program to implement this as frame-based code can be found in the ccs\
FiltFrame directory of Chapter 7. Be sure to inspect the full code listings in this di-rectory to understand the complete working of the program.2 Here we simply point out some highlights. The discussion which follows assumes you have read and are familiar with the explanation of frame-based processing given in Chapter 6.
Realistically, you would use MATLAB or some other filter design program to determine your filter coefficients. To easily use FIR filter coefficients generated by the filter design tools in MATLAB, you can use the script file named fir_dump2c.m that is located in the MatlabExports directory for Appendix E (there are other script files in the same directory for IIR filters). As discussed in Chapter 3, this script creates two files needed by your C program, typically named coeff.h and coeff.c. These files define N , representing the order of the filter, and B[N + 1], the array of filter coefficients.
The program we provide makes use of the EDMA capabilities of the DSK. This C code is almost identical to the EDMA version of the frame-based C code described in Chapter 6.
The only differences are the need to include coeff.h and coeff.c in your project, and the changes described below to the ProcessBuffer() routine in the ISRs.c file. The contents of coeff.h and coeff.c were discussed in Chapter 3. The code for the ProcessBuffer() routine is shown in Listing 7.1.
2Look also in the DSK_Support.c file in the common_code directory for initialization functions shared by multiple programs.
144 CHAPTER 7. DIGITAL FILTERS USING FRAMES Listing 7.1: ProcessBuffer() routine for implementing a frame-based FIR filter.
void ProcessBuffer ( ) {
2 Int16 ∗pBuf = buffer [ ready_index ] ;
// extra buffer room for convolution " edge effects "
4 // N is filter order from coeff .h
// offset pointers to start filling after N elements
12 pR += N ; pL += N ;
14
// extract data to float buffers
16 f o r ( i = 0 ; i < BUFFER_COUNT ; i++) {
// order is important here : must go right first then left
18 ∗pR++ = ∗pBuf++;
∗pL++ = ∗pBuf++;
20 }
22 // reinitialize pointer before FOR loop pBuf = buffer [ ready_index ] ;
24
// //////////////////////////////////////
26 // Implement FIR filter
// Ensure COEFF .C is part of project
28 // //////////////////////////////////////
f o r ( i =0; i < BUFFER_COUNT ; i++){
30 yLeft = 0 ; // initialize the L output value
yRight = 0 ; // initialize the R output value
32
f o r ( j=0 ,k=i ; j <= N ; j++,k++){
34 yLeft += Left [ k ] ∗ B [ j ] ; // perform the L dot - product yRight += Right [ k ] ∗ B [ j ] ; // perform the R dot - product
36 }
38 // pack into buffer after bounding ( first right then left )
∗pBuf++ = _spint ( yRight ∗ 65536) >> 1 6 ;
40 ∗pBuf++ = _spint ( yLeft ∗ 65536) >> 1 6 ; }
42
// save end values at end of buffer array for next pass
44 // by placing at beginning of buffer array
f o r ( i=BUFFER_COUNT , j =0; i < BUFFER_COUNT+N ; i++,j++){
46 Left [ j ]= Left [ i ] ; Right [ j ]= Right [ i ] ;
48 }
7.5. FOLLOW-ON CHALLENGES 145
50 // ////// end of FIR routine ///////////
52 // reinitialize pointer pBuf = buffer [ ready_index ] ;
54
buffer_ready = 0 ; // signal we are done
56 }
The key parts of the code that differ the EDMA code in Chapter 6 are:
• (lines 5–6) declaring the arrays to be BUFFER_COUNT+N, which leaves enough room for the edge values that need to be saved;
• (lines 12–13) advancing the pointers so that the incoming data doesn’t overwrite the edge values from the previous frame;
• (lines 33–36) using different array index values to implement the convolution properly across the frame; and
• (lines 45–48) copying the edge values of the current frame to the beginning of the buffer so they will be available when the next frame comes in.
Now that you understand the code. . .
Go ahead and copy all of the files into a separate directory. If you wish to design your own FIR filter using MATLAB rather than using the provided filter (a simple low-pass filter), use the script file fir_dump2c.m that can be found in the MatlabExports directory of Appendix E and was first discussed in Chapter 3. Copy any new versions of coeff.h and coeff.c that you create into your project directory before proceeding. When ready, open the project in CCS and “Rebuild All.” Once the build is complete, “Load Program” into the DSK and click on “Run.” Your frame-based FIR filter is now running on the DSK.
7.5 Follow-On Challenges
Consider extending what you have learned.
1. Compare the sample-based FIR code to the frame-based FIR code. Which do you predict could handle the higher-order filter in real time? Try to get the sample-based FIR code to break the real-time schedule by using a large-order filter; try the same filter with the frame-based code.
2. Many FIR filters exhibit some form of symmetry in the coefficients to ensure linear phase response. For example, b[0] = b[N− 1], b[1] = b[N − 2], b[2] = b[N − 3], and so on. Modify the code provided in this chapter to take advantage of this symmetry.
3. In addition to symmetry, some FIR filters also exhibit a regular pattern of zero-valued coefficients (such as every other value). Modify the code provided in this chapter to take advantage of this fact.
4. This chapter demonstrated an FIR filter using frames. Implement an IIR filter using frames.
146 CHAPTER 7. DIGITAL FILTERS USING FRAMES
7.6 Problems
1. Suppose an FIR filter of order N = 30 is implemented using frames. Assume the sample frequency is Fs= 48 kHz, and the frame size is 1024 (per channel) as defined by BUFFER_COUNT in the C code. If the “edge effects” described in this chapter are not avoided by increasing the size of the Left and Right buffers, how many times per second would a “clicking” or “popping” noise (due to edge effects) be heard in the output?
2. Suppose an FIR filter of order N = 30 is implemented using frames. Assume the sample frequency is Fs= 48 kHz, and the frame size is 1024 (per channel) as defined by BUFFER_COUNT in the C code. Further assume that the “edge effects” described in this chapter are avoided by increasing the size of the Left and Right buffers as needed, but that the programmer neglected the step of copying the end values of the buffers to the beginning of the buffers before the new frame data arrives. How many times per second would the output be in error, and for how long would it be in error?
3. Assume the sample frequency is Fs= 48 kHz, and the frame size is 1024 (per channel) as defined by BUFFER_COUNT in the C code for the frame-based approach. Compare the time available to the DSP to implement an FIR filter (on both the left and right channels) using a frame-based approach versus a sample-by-sample approach. Neglect overhead in your answer.