Engineering 71 Project

Multi-channel FIR Audio Filter Implementation on a Blackfin DSP

Aron Dobos

25 April 2006

 

 

Abstract

 

High and low pass linear phase FIR filters were designed to split an audio input signal into frequencies appropriate for satellite speakers and a subwoofer.  Mixing and amplitude scaling were also implemented to provide full user control over the balance between the four surround channels and subwoofer.  The signal processing system was implemented on an Analog Devices Blackfin BF533 DSP evaluation board, and tested using a 5 channel power amplifier system built previously by the author for the Engineering 93 Directed Study project. 

 

System Components

 

The complete audio system consists of four main components: the discrete transistor power amplifiers, power supply and power controller circuit, the user interface circuit, and the audio DSP board.  A block diagram is shown below in Figure 1.

 

Figure 1. Audio System Block Diagram

 

The DSP board takes as input the four audio input channels as well as an RS-232 serial connection from the interface controller circuit.  The RS-232 link allows changing of the volume and balance levels in the DSP code while the system is operating.  The most recent settings are saved in a FLASH memory by the user interface controller and are restored upon power up.  The rest of this report concerns itself only with the DSP component of the audio system.

 

DSP Hardware

 

The Analog Devices BF533-EZKIT development board provides a RISC-like 16-bit fixed point DSP processor, 32 MB SDRAM, an AD1836 96 kHz audio codec with 4 input and 6 output channels, a video codec, and the necessary hardware for the RS-232 communication.  The development board also has a USB interface for programming the on board FLASH memory, as well as providing a complete PC-host controlled debugging interface.  The Blackfin BF533 DSP core can operate at up to 600 MHz, and includes two 16-bit MACs, two 40-bit ALUs, and four 8-bit video ALUs, as well as up to 148k on-chip SRAM.  In terms of peripherals, it provides a parallel peripheral interface (PPI), two dual channel synchronous serial ports, three timer/counters, RS-232 support, SPI port, real-time clock, and on-chip JTAG debugging.

 

The development board is used as provided with no modifications.  The analog audio input and output signals are connected to the board through the standard mono RCA connectors, and the DB-9 port is used for the serial link. 

 

The AD1836 codec is configured to run with a 48 kHz sampling rate.  As each data sample is digitized, the codec generates an interrupt in the DSP processor, and the data samples are read from the direct memory access (DMA) configured serial port into the buffers.  The interrupt handler then calls the data processing routines, and at the end of the processing, the output data samples are copied to the D/A converter data memory locations.  A block diagram of this processing scheme is shown in Figure 2.

Figure 2. Hardware Configuration for DSP/Codec

 

Filter Design in MATLAB

 

A FIR filter is chosen for the audio application due to its ability to provide linear phase response.  The coefficients are calculated using MATLAB’s Signal Processing Toolbox.  Due to the very narrow transition band desired relative to the sampling frequency of 48 kHz, the filter requires a very large number of taps to achieve acceptable levels of passband ripple and stopband attenuation. 

 

For the subwoofer’s low pass filter, a center transition band frequency is chosen to be 130 Hz with +/- 60 Hz deviations from that point defining the passband and stopband edges.  The satellite speaker high pass filter is designed with the same 120 Hz transition band, but centered around 100 Hz instead.  The filter design was iterated with increasing numbers of taps until the desired response was achieved.  The final number of taps used is 350.  The code also writes the filter coefficients in Q1.15 fractional representation format into a text file for inclusion in the DSP code.  The MATLAB source is included, as well as the frequency response of the filters.

 

% dsp filter coefficent calculations

 

clc;

ntaps = 350; % adjust for desired passband/stopband performance

fs = 48000;

 

fxlp = 130;  % lowpass transition freq

fxhp = 100;  % highpass transition freq

df = 60;

 

blp = firpm(ntaps, [ 0 (fxlp-df)/(fs/2) (fxlp+df)/(fs/2) .5]*2, [ 1 1 0 0]);

bhp = firpm(ntaps, [ 0 (fxhp-df)/(fs/2) (fxhp+df)/(fs/2) .5]*2, [ 0 0 1 1]);

 

[hlp wlp] = freqz(blp, 1, 2048);

[hhp whp] = freqz(bhp, 1, 2048);

plot(wlp/(2*pi)*(fs/2),db(abs(hlp)), whp/(2*pi)*(fs/2),db(abs(hhp)));

title('FIR Filter Frequency Responses');

xlabel('Frequency (Hz)');

ylabel('Magnitude (dB)');

axis([0 800 -60 3]);

grid;

 

% dump the coefficients to text files

fp1 = fopen(sprintf('firlp%d.txt', ntaps), 'w');

fp2 = fopen(sprintf('firlp%dq15.txt', ntaps), 'w');

for i=1:length(blp),

    comma = ',';

    if (i==length(bhp))

        comma = '';

    end

    fprintf(fp1, '%f%s\r\n', blp(i), comma);

    fprintf(fp2, '%d%s\r\n', round(2^15 * blp(i)), comma);

end

fclose(fp1);

fclose(fp2);

 

fp1 = fopen(sprintf('firhp%d.txt', ntaps), 'w');

fp2 = fopen(sprintf('firhp%dq15.txt', ntaps), 'w');

for i=1:length(bhp),

    comma = ',';

    if (i==length(bhp))

        comma = '';

    end

    fprintf(fp1, '%f%s\r\n', bhp(i), comma);

    fprintf(fp2, '%d%s\r\n', round(2^15 * bhp(i)), comma);

end

fclose(fp1);

fclose(fp2);

disp('wrote coefficients to text files.');

 

 

Figure 3. FIR Filter Frequency Responses

 

Using the Remez exchange algorithm for optimal equiripple filter design, linear phase filters with about 0.15 dB passband ripple and about -35 dB stopband attenuation are achieved using 350 taps.

 

Audio Processing

 

The audio processing scheme is show below.

 

Figure 4. Audio Processing Layout

 

Each input channel has an independently controlled scaling function to allow the audio balance in the room to be appropriately adjusted for the relative positions of the satellite speakers.  The scaled channel outputs are summed into a node that is low pass filtered for the subwoofer output.  Low frequency sounds are non-directional, so it is not a problem for the low frequencies to arise from a single location.  The satellite speaker channels are high pass filtered to protect the midrange drivers and tweeters, and a final scaling is applied to all channels as a master volume control.

 

Software Implementation

 

The VisualDSP++ software development tool provided with the evaluation board was used to program the processor using the standard C language.  The framework and initialization code for the processor and codec were pulled straight from the ‘Audio Talkthrough’ example project provided with the development tools.  The FIR filters were implemented using the built-in library functions that are written to take advantage of the DSP hardware capabilities.  The delay buffers were preallocated, and the filtering was done on one sample at a time at a 48 kHz rate.  The computational burden on the DSP of computing five 350 tap filters and performing the required scaling was not estimated, but the use of fixed point fractional representation combined with the relatively high throughput of the DSP probably rendered the task feasible.  No problems could be heard in the audio.

 

The codec provides the sampled audio data as 24 bit signed integers.  However, the signal processing algorithms were implemented in 16 bits fractional representation to maximize the native performance of the DSP, at the expense of 8 bits of precision.  To complicate matters further, the 24 bit audio data arrives in 32 bit integers.  The simplest way to convert to a 16 bit Q1.15 fractional representation is to simply shift the 32 bit integer to the right 16 times.  This way, the most significant bit is still preserved in the reduced representation.  After the signal processing is completed, the data values are shifted left 16 times and filled with zeros. 

 

The main function simply idles in a loop awaiting reads from the RS-232 port.  Upon receiving a volume level command, the appropriate scaling factor is adjusted.  The signal processing is done in the interrupt service routine, so the RS-232 communication is simply interrupted until it is completed.  The relevant sections of C source code are presented next.

 

Audio Signal Processing Code

extern fir_state_fr16 firstate_FL;

extern fir_state_fr16 firstate_FR;

extern fir_state_fr16 firstate_RL;

extern fir_state_fr16 firstate_RR;

extern fir_state_fr16 firstate_SUB;

 

extern fract16 levelFL, levelFR, levelRL, levelRR, levelSUB, masterVOL;

 

static fract16 inFL16, inFR16, inRL16, inRR16, inSUB16;

static fract16 outFL16, outFR16, outRL16, outRR16, outSUB16;

 

void Process_Data(void)

{

      // align the bits for fractional processing

      inFL16 = inFL >> 16;

      inFR16 = inFR >> 16;

      inRL16 = inRL >> 16;

      inRR16 = inRR >> 16;

           

      // perform the channel level scaling

      inFL16 = mult_fr1x16(levelFL, inFL16);

      inFR16 = mult_fr1x16(levelFR, inFR16);

     

      inRL16 = mult_fr1x16(levelRL, inRL16);

      inRR16 = mult_fr1x16(levelRR, inRR16);

     

      // sum the scaled channels for the subwoofer

      inSUB16 = add_fr1x16(add_fr1x16(add_fr1x16(inFL16, inFR16), inRL16), inRR16);

 

      // high-pass filter the satellite channels

      fir_fr16( &inFL16, &outFL16, 1, &firstate_FL );

      fir_fr16( &inFR16, &outFR16, 1, &firstate_FR );

     

      fir_fr16( &inRL16, &outRL16, 1, &firstate_RL );

      fir_fr16( &inRR16, &outRR16, 1, &firstate_RR );

                 

      // low-pass filter the subwoofer output

      fir_fr16( &inSUB16, &outSUB16, 1, &firstate_SUB);

 

      // scale the subwoofer output

      outSUB16 = mult_fr1x16(outSUB16, levelSUB);

     

      // scale all channels with the master volume setting

     

      outFL16 = mult_fr1x16(masterVOL, outFL16);

      outFR16 = mult_fr1x16(masterVOL, outFR16);

      outRL16 = mult_fr1x16(masterVOL, outRL16);

      outRR16 = mult_fr1x16(masterVOL, outRR16);

      outSUB16 = mult_fr1x16(masterVOL, outSUB16);

     

      outFL = outFL16 << 16;

      outFR = outFR16 << 16;

      outRL = outRL16 << 16;

      outRR = outRR16 << 16;

      outSUB = outSUB16 << 16; 

     

      outSUB *= 4; // increase the bass output to provide desired boost

}

 

TDM-Mode Serial Port (SPORT0) Interrupt Handler

EX_INTERRUPT_HANDLER(Sport0_RX_ISR)

{

      // confirm interrupt handling

      *pDMA1_IRQ_STATUS = 0x0001;

 

      // copy input data from DMA input buffer into variables

      inFL = iRxBuffer1[INTERNAL_ADC_L0];

      inFR = iRxBuffer1[INTERNAL_ADC_R0];

 

      inRL = iRxBuffer1[INTERNAL_ADC_L1];

      inRR = iRxBuffer1[INTERNAL_ADC_R1];

 

      // call function that contains signal processing code

      Process_Data();                    

 

      // copy processed data from variables into DMA output buffer

      iTxBuffer1[INTERNAL_DAC_L0] = outFL;     

      iTxBuffer1[INTERNAL_DAC_R0] = outFR;

     

      iTxBuffer1[INTERNAL_DAC_L1] = outRL;     

      iTxBuffer1[INTERNAL_DAC_R1] = outRR;

     

      iTxBuffer1[INTERNAL_DAC_L2] = outSUB;    

      iTxBuffer1[INTERNAL_DAC_R2] = 0;

}

 

‘main’ Function, Filter Variable Declarations and Initialization, RS-232 Communication Code

// declare the filter coefficient vectors

fract16 H_lp[NTAPS] = {

#include "firlpq15.txt"

};

 

fract16 H_hp[NTAPS] = {

#include "firhpq15.txt"

};

 

// declare the filter delay line buffers

fract16 filt_FL[NTAPS];

fir_state_fr16 firstate_FL;

 

fract16 filt_FR[NTAPS];

fir_state_fr16 firstate_FR;

 

fract16 filt_RL[NTAPS];

fir_state_fr16 firstate_RL;

 

fract16 filt_RR[NTAPS];

fir_state_fr16 firstate_RR;

 

fract16 filt_SUB[NTAPS];

fir_state_fr16 firstate_SUB;

 

fract16 levelFL, levelFR, levelRL, levelRR, levelSUB, masterVOL;

 

void main(void)

{

      int result = 0;

      int chan, vol;

     

      masterVOL = 0.1;

      levelFL = levelFR = levelRL = levelRR = levelSUB = FRACT16_MAX;

     

      sysreg_write(reg_SYSCFG, 0x32);           //Initialize System Configuration Register

     

      UART_DevEntry.DeviceID = UART_DEVICEID;

      UART_DevEntry.data     = (void *) BAUDRATE;

     

      // Insert this new device into the device table.

      result = add_devtab_entry ( &UART_DevEntry );

     

      set_default_io_device( UART_DEVICEID );

     

      // Redirect Stdio for printf and scanf

      FILE *fp;

      fp = freopen("", "a+", stdin);

      fp = freopen("", "a+", stdout);    

     

      // initialize filter states

      fir_init(firstate_FL, H_hp, filt_FL, NTAPS, 1);

      fir_init(firstate_FR, H_hp, filt_FR, NTAPS, 1);

      fir_init(firstate_RL, H_hp, filt_RL, NTAPS, 1);

      fir_init(firstate_RR, H_hp, filt_RR, NTAPS, 1);

      fir_init(firstate_SUB, H_lp, filt_SUB, NTAPS, 1);    

     

      // call the initialization functions

      Init_EBIU();

      Init_Flash();

      Init1836();

      Init_Sport0();

      Init_DMA();

      Init_Sport_Interrupts();

      Enable_DMA_Sport0();   

     

      // flash memory addresses of LEDs (port b)

#define pFlashA_PortB_Dir     (volatile unsigned char *)0x20270007

#define pFlashA_PortB_Data    (volatile unsigned char *)0x20270005

 

      // set pin direction of port b flash

      *pFlashA_PortB_Dir = 0x3f;   

      *pFlashA_PortB_Data = 0xFF;

     

      while(1)

      {

            static unsigned char led;

            int count, i;

           

            scanf("%d:%d", &chan, &vol);

           

            if (vol > 50)

                  vol = 50;

            if (vol < 0)

                  vol = 0;

           

            if (chan > 5)

                  chan = 5;

            if (chan < 0)

                  chan = 0;

                 

           

            fract16 flevel16;

            float percent;

            if (vol == 50)

            {

                  percent = 1.0;

                  flevel16 = FRACT16_MAX;

            }

            else

            {

                  percent = ((float)vol) / 50.0;

                  /* square law vol increase for sensitivity at low vols - should probably be logarithmic scale */         

                  flevel16 = float_to_fr16( percent*percent );

            }

           

            switch(chan)

            {

            case 0:

                  masterVOL = flevel16;

                  break;

            case 1:

                  levelFL = flevel16;

                  break;

            case 2:

                  levelFR = flevel16;

                  break;

            case 3:

                  levelRL = flevel16;

                  break;

            case 4:

                  levelRR = flevel16;

                  break;

            case 5:

                  levelSUB = flevel16;

                  break;                 

            }

                 

            count = (int)(percent * 5.0);

            led = 0;

            for ( i=0;i<count && i<8;i++)

            {

                  led |= 0x1;

                  led = led << 1;

            }

            *pFlashA_PortB_Data = led;

      }

}

 

Conclusions and Future Work

 

Real-time digital filters were successfully designed and implemented on the Analog Devices Blackfin processor.  Although the current code works reasonably well, there are some saturation problems at higher volumes, and it is not currently possible to achieve greater than unity signal gain due to the range of the Q1.15 data format.  One potential direction for this project would be to use the Visual Audio development tool from Analog Devices.  Using VisualAudio, it would be possible to characterize the processing burden on the current implementation, as well as building up a signal processing path from library modules that include volume controls, tone controls, FIR and IIR filtering capabilities, and mixing functions.  The filter coefficients would still be calculated in MATLAB.  If IIR filters were used at the expense of linear phase, sharper cutoffs could be achieved with fewer parameters, and more bits could be used to avoid some of the limitations of the 16 bit fractional representation currently in use, while still achieving very high performance.

 

Completed Audio System Picture

 

Figure 5. Completed Audio System