Subversion Repositories shark

Rev

Rev 1086 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* Project:     HARTIK 3.0                                      */
/* Description: Hard Real TIme Kernel for 386 & higher machines */
/* Author:      Paolo Gai <pgai@rsk.it>                         */
/*              Advanced Linux Sound Architecture (ALSA)        */
/*              Copyright (c) by Jaroslav Kysela <perex@jcu.cz> */
/*              Luca Abeni                                      */
/*              FFTW by M. Frigo and S. G. Johnson              */

/* Date:        08/09/1999                                      */

/* File:        fftplay.c                                       */
/* Revision:    1.00 (Kernel 0.1.4; Library 0.0.9; Util 0.0.4)  */


/*

FFTPlay 1.0
-----------

This application reads data from the audio microphone and then put it
to the screen in a graphical oscilloscope-like form (both standard Hartik
SB driver and Alsa driver can be used).

The application also calculate a FFT on the latest values, and displays
the power spectrum in tho ways, one like an equalizator, and the other in a
2D form.

A resolution of 1024x768, 64K colors is used.

The task set is composed by these tasks and functions:

Self buffering functions (SB only)
----------------------------------
this function takes the samples read by the mic and then makes a window
with the last WINDATA_NSAMPLES samples. The window is then put in
the CAB windata.

Task raw and task mixer (ALSA only)
-----------------------------------
These tasks are used with the Alsa driver; because it doesn't support the
self-buffering mode, we have to do a forever cycle in witch we have to read
all the data. Then the data are managed like the self-buffering functions of
the SB driver or sent to the mixer task (with a STREAM mailbox) which makes
the window.
(This approach is not good for realtime...)

Task wave
---------
This task read the last window and then put it on the screen in a wave form
on the top of the screen.
The task's period is set to 40 ms (25 fps).

Task fft
--------
This task read the last window and then it computes the FFT.
With the FFT data it computes the power spectrum, whitch is sent to the
CAB pwrdata.
The task's period is set to 10 ms (good for the 2D story task).
This task is the only Hard Task.

Task equ
--------
This task read the last power spectrum and displays it in a graphical
form, like a hi-fi equalizator.
The Histograms can be white or coloured like the equ2D task (see EQU_SHADE)
The task's period is set to 40 ms (25 fps).

Task equ2D
----------
This task read the lasf power spectrum and displays it in a graphical
one-line form. Each pixel is a power coefficient, and its colour shade
from black (no power) to red (high power) passing through green and blue.
The task display the last EQU2D_WIDTH power lines.
The task's period is set to 10 ms (good for the 2D story task).

****************************************************************************
TASK LOAD
****************************************************************************

                     period              wcet
task             tick     (ms)      us          %
--------------------------------------------------------
sound driver    24->3    12->1.5   200     0.016->0.133
wave              80       40     11500       0.2875
fft               20       10      3000       0.3000
equ               80       40      7000       0.1750
equ2D             20       10       500       0.0500
                                          -------------
                                              0.812 (last 4)
*/


#include <ll/ll.h>

#include <kernel/types.h>
#include <kernel/model.h>
#include <kernel/func.h>

#include <modules/cabs.h>

#include <string.h>
#include <stdlib.h>
#include <semaphore.h>

#include <drivers/keyb.h>
#include <drivers/crtwin.h>
#include <drivers/glib.h>
#include <drivers/sound.h>
#include <ports/rfftw.h>

/* now the load constants... */

#define WCET_WAVE   11500
#define WCET_FFT    3000
#define WCET_EQU    500
#define WCET_EQU2D  500

#define PERIOD_WAVE  40000
#define PERIOD_FFT   10000
#define PERIOD_EQU   40000
#define PERIOD_EQU2D 10000

/* define if shorts critical sections wanted */
#define SHORT_CRITICAL_SECTIONS(x) \
  if (!((x)%64))                   \
    {                              \
      sem_post(&mutex);            \
      sem_wait(&mutex);            \
    }


/* define if you want NRT or SOFT... */
#define TASK_TYPE SOFT
//#define TASK_TYPE NRT



/* Only 4 Debug... */
/*#define NO_GRX */

/* Samples are 16-bit signed integers */
typedef short SAMPLE;
#define MAX_SAMPLE 32768

/* Informations about the sampling rate and buffers */
WORD rawdata_nsamples;
WORD rawdata_buffer_size;
WORD rawdata_freq;

/* Numbers of samples of the sample window */
#define WINDATA_NSAMPLES  1024

/* task WAVE */
/* the point (wave_x,wave_y) is on the center left of the area... */
#define WAVE_NSAMPLES 1024
#define WAVE_X        0
#define WAVE_Y        130
#define WAVE_HEIGHT   80

/* task FFT */
#define FFT_NSAMPLES  WINDATA_NSAMPLES
#define PWR_NSAMPLES  (FFT_NSAMPLES/2+1)

/* task EQU */
/* the point (equ_x, equ_y) is the top right corner */
#define EQU_NSAMPLES  PWR_NSAMPLES
#define EQU_X         170
#define EQU_Y         255
#define EQU_HEIGHT    170
#define EQU_SHADE

/* task EQU2D */
/* the point (equ2d_x, equ2d_y) is the top left corner */
#define EQU2D_NSAMPLES  EQU_NSAMPLES
#define EQU2D_X         255
#define EQU2D_Y         EQU_Y
#define EQU2D_WIDTH     768
#define EQU2D_CLIP      255

/* scenario */
#define SCENARIO_NLABEL 16

/* Scale factors */
#define FFT_SCALE   (16384.0)
#define EQU_SCALE   (32.0)
#define EQU2D_SCALE (8.0)
//#define EQU_SCALE   (64.0)
//#define EQU2D_SCALE (16.0)


/* CAB ports... */
CAB cab_windata;   /* a window on the last WINDATA_DIM samples */
CAB cab_pwrdata;   /* the last power spectrum                  */

/* for the cab_windata */
typedef struct {
  int start;
  SAMPLE sample[WINDATA_NSAMPLES];
} window;

/* for the cab_pwrdata */
typedef struct {
  fftw_real p[PWR_NSAMPLES];
} power;

/* graphic mutex... */
sem_t mutex;

// win is global... because is used by raw_infun...
window win;



/* useful colors... */
int white;
int black;
int red;

static void version( void )
{
  cprintf( "Hartik FFT Play 1.0\n"     );
  cprintf( "-----------------------\n" );
  cprintf( "by Paolo Gai 1999\n"       );
  cprintf( "   <pj@hartik.sssup.it>\n" );
  cprintf( "-----------------------\n" );
}

void reverse(char s[])
{
  int c, i, j;

  for (i = 0, j = strlen(s)-1; i<j; i++, j--)
  {
    c = s[i];
    s[i] = s[j];
    s[j] = c;
  }
}

char * itoa(int n, char *s)
{
  int i, sign;

  if ((sign = n) < 0)
    n = -n;

  i = 0;

  do
  {
    s[i++] = n % 10 + '0';
  } while ((n /= 10) > 0);

  if (sign < 0)
    s[i++] = '-';

  s[i] = 0;

  reverse(s);

  return s;
}



/*
   This is the self-buffering function: read the samples and put their mean
   value in a CAB
*/

int raw_infun(void *b)
{
    int i;
    char *w;
    SAMPLE *audiobuf = (SAMPLE *)b;

    for (i=0; i<rawdata_nsamples/2; i++) {
      win.sample[win.start] = audiobuf[i];
      win.start = (win.start+1) % WINDATA_NSAMPLES;
    }

    w = cab_reserve(cab_windata);
    memcpy(w, &win, sizeof(window));
    cab_putmes(cab_windata,w);

  #if defined(NO_GRX)
    cprintf("X"); //"XXX%d\n",win.sample[win.start]);
  #endif
    return 0;
}



void init_rawdata()
{
  int i;
  char *w;

  win.start = 0;
  for (i=0; i<WINDATA_NSAMPLES; i++)
    win.sample[i] = 0;

  w = cab_reserve(cab_windata);
  memcpy(w, &win, sizeof(window));
  cab_putmes(cab_windata,w);
}





TASK wave_task()
{
  window *p;
  int x,y;
  int s;

  while(1)
  {
    p = (window *)cab_getmes(cab_windata);

    /* let's print the wave */
    sem_wait(&mutex);
    for(x = WAVE_X, s = p->start;
        x < WAVE_X+WAVE_NSAMPLES;
        x++, s = (s+1)%WINDATA_NSAMPLES )
    {
      y = WAVE_Y + (WAVE_HEIGHT * p->sample[s]) / MAX_SAMPLE;
      SHORT_CRITICAL_SECTIONS(x);
      grx_plot(x,y,white);
    }
    sem_post(&mutex);

    task_endcycle();

    /* let's erase the wave */
    sem_wait(&mutex);
    for(x = WAVE_X, s = p->start;
        x < WAVE_X+WAVE_NSAMPLES;
        x++, s = (s+1)%WINDATA_NSAMPLES )
    {
      y = WAVE_Y + (WAVE_HEIGHT * p->sample[s]) / MAX_SAMPLE;
      SHORT_CRITICAL_SECTIONS(x);
      grx_plot(x,y,black);
    }
    sem_post(&mutex);

    cab_unget(cab_windata,(char *)p);

  }
}


rfftw_plan plan;

void fft_close(void *arg)
{
  rfftw_destroy_plan(plan);
}


TASK fft_task()
{
  fftw_real in[FFT_NSAMPLES], out[FFT_NSAMPLES];
  power power_spectrum;

  #if defined(NO_GRX)
    fftw_real max = 0.0;
  #endif

  char *m;

  int k, i;

  window *p;

  plan = rfftw_create_plan(FFT_NSAMPLES, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE);

  sys_atrunlevel(fft_close, NULL, RUNLEVEL_BEFORE_EXIT);

  while(1)
  {
    /* Let's prepare the intput FFT data */
    p = (window *)cab_getmes(cab_windata);

    for (k = 0, i = p->start;
        k < FFT_NSAMPLES;
        k++, i = (i+1)%WINDATA_NSAMPLES)
      in[k] = p->sample[i]/FFT_SCALE;

    cab_unget(cab_windata,(char *)p);

    /* zero-padding if needed */
    for (k=WINDATA_NSAMPLES; k < FFT_NSAMPLES; k++)
      in[k] = 0.0;

    rfftw_one(plan, in, out);

    /* power spectrum computation */
    power_spectrum.p[0] = out[0]*out[0];  /* DC component */
    for (k = 1; k < PWR_NSAMPLES; ++k)  /* (k < N/2 rounded up) */
          power_spectrum.p[k] = out[k]*out[k] + out[FFT_NSAMPLES-k]*out[FFT_NSAMPLES-k];
    if (FFT_NSAMPLES % 2 == 0) /* N is even */
          power_spectrum.p[FFT_NSAMPLES/2] = out[FFT_NSAMPLES/2]*out[FFT_NSAMPLES/2];  /* Nyquist freq. */

    m = cab_reserve(cab_pwrdata);
    memcpy(m, &power_spectrum, sizeof(power));
    cab_putmes(cab_pwrdata,m);

    #if defined(NO_GRX)
    max = 0.0;
    for (k=0; k<PWR_NSAMPLES; k++)
      if (power_spectrum.p[k] > max)
        max = power_spectrum.p[k];

    //cprintf("%f %f\n",max,(max / EQU_SCALE) );
    #endif

    task_endcycle();

  }
}

/* structure is like the wave task... */
TASK equ_task()
{
  power *p;

  int x[PWR_NSAMPLES];
  int y;
  int s;

  int r,g,b;

  while(1)
  {
    p = (power *)cab_getmes(cab_pwrdata);

    /* print the lines */
    sem_wait(&mutex);
    for(y = EQU_Y, s = 0;
        s < EQU_NSAMPLES;
        y++, s++ )
    {
      x[s] = (int)(p->p[s] / EQU_SCALE);

      if (x[s] > EQU_HEIGHT)
        x[s] = EQU_HEIGHT;

      x[s] = EQU_X - x[s];


      #if defined(EQU_SHADE)

       /* like the task equ2d... */
       r = (int)(p->p[s] / EQU2D_SCALE);
       if (r > EQU2D_CLIP)
         r = EQU2D_CLIP;

            if (r< 64)  g = r * 4;
       else if (r<128)  g = (128-r) * 4;
       else             g = 0;

            if (r<128)  b = 0;
       else if (r<192)  b = (r-128) * 4;
       else             b = (256-r) * 4;

       SHORT_CRITICAL_SECTIONS(y);
       grx_line(EQU_X,y,x[s],y,rgb16(r,g,b));
      #else
       SHORT_CRITICAL_SECTIONS(y);
       grx_line(EQU_X,y,x[s],y,white);
      #endif
    }
    sem_post(&mutex);

    task_endcycle();

    /* erase the lines... */
    sem_wait(&mutex);
    for(y = EQU_Y, s = 0;
        s < EQU_NSAMPLES;
        y++, s++ )
    {
      SHORT_CRITICAL_SECTIONS(y);
      grx_line(EQU_X,y,x[s],y,black);
    }
    sem_post(&mutex);

    cab_unget(cab_pwrdata,(char *)p);

  }
}

TASK equ2d_task()
{
  power *p;

  int pwrint;

  int x = 0;

  int y,s;

  int r,g,b;

  while(1)
  {

    p = (power *)cab_getmes(cab_pwrdata);

    /* print the line */
    sem_wait(&mutex);

    for(y = EQU2D_Y, s = 0;
        s < EQU2D_NSAMPLES;
        y++, s++ )
    {
      pwrint = (int)(p->p[s] / EQU2D_SCALE);

      if (pwrint > EQU2D_CLIP)
        pwrint = EQU2D_CLIP;

      r = pwrint;

           if (pwrint< 64)  g = pwrint * 4;
      else if (pwrint<128)  g = (128-pwrint) * 4;
      else                  g = 0;

           if (pwrint<128)  b = 0;
      else if (pwrint<192)  b = (pwrint-128) * 4;
      else                  b = (256-pwrint) * 4;

      SHORT_CRITICAL_SECTIONS(y);
      grx_plot(EQU2D_X+x,y,rgb16(r,g,b));
    }

    x = (x+1) % EQU2D_WIDTH;
    grx_line(EQU2D_X+x,EQU2D_Y,EQU2D_X+x,EQU2D_Y+EQU2D_NSAMPLES,white);

    sem_post(&mutex);

    cab_unget(cab_pwrdata,(char *)p);

    task_endcycle();
  }
}

TASK prova_task()
{
  window *p;

  while(1)
  {
    p = (window *)cab_getmes(cab_windata);
    cprintf("%d %d %d\t", p->start /*sample[0]*/,p->sample[1],p->sample[2]);
    cab_unget(cab_windata,(char *)p);

    task_endcycle();
  }
}


void scenario(int f)
{
  int i,y;
  char s[6];

  grx_line(0,WAVE_Y-WAVE_HEIGHT-1,1023,WAVE_Y-WAVE_HEIGHT-1,red);
  grx_line(0,WAVE_Y+WAVE_HEIGHT+1,1023,WAVE_Y+WAVE_HEIGHT+1,red);
  grx_line(0,EQU_Y-11            ,1023,EQU_Y-11            ,red);



  /* lines near the frequencies */
  grx_line(EQU_X  +1,EQU_Y,EQU_X  +1,EQU_Y+EQU_NSAMPLES,red);
  grx_line(EQU2D_X-1,EQU_Y,EQU2D_X-1,EQU_Y+EQU_NSAMPLES,red);

  for (i=0; i<SCENARIO_NLABEL; i++)
  {
    y = (i*EQU_NSAMPLES)/(SCENARIO_NLABEL-1);
    if (i == SCENARIO_NLABEL-1) y--;
    grx_line(EQU_X  +1,EQU_Y+y,EQU_X  +10,EQU_Y+y,red);
    grx_line(EQU2D_X-1,EQU_Y+y,EQU2D_X-10,EQU_Y+y,red);

    itoa((i*f)/(SCENARIO_NLABEL-1),s);
    grx_text(s,EQU_X+20,EQU_Y+y-8,white,black);
  }

  grx_text("FFTPlay 1.0 - by Paolo Gai 1999 <pj@hartik.sssup.it>", 0,8, rgb16(0,255,0), black );
  grx_text("...press ENTER key to exit..."  , 0,24, rgb16(0,255,0), black );

  grx_text("FFT Power Spectrum", 0      , EQU_Y-21, rgb16(0,0,255), black);
  grx_text("FFT Power Story", EQU2D_X+16, EQU_Y-21, rgb16(0,0,255), black);
  grx_text("Waveform"   , 0, WAVE_Y-WAVE_HEIGHT-10, rgb16(0,0,255), black);

}




void compute_params(int *freq,WORD *nsamp, WORD *per)
{
  if (*freq< 2000)
  {
    cprintf("WARNING: frequency less than 2000Hz\n  ---> frequency set to 2000Hz\n");
    *freq = 2000;
  }
  if (*freq<= 8000) { *nsamp = 128; *per = 10; return; }
  if (*freq<=16000) { *nsamp = 256; *per = 10; return; }
  if (*freq<=24000) { *nsamp = 512; *per = 10; return; }
  if (*freq>48000)
  {
    cprintf("WARNING: frequency greather than 48000Hz\n  ---> frequency set to 48000Hz\n");
    *freq = 48000;
  }
  if (*freq<=48000) { *nsamp = 1024;*per = 10; return; }
}



void my_close(void *arg)
{
        grx_close();
}


void endfun(KEY_EVT *k)
{
    cprintf("Ctrl-Brk pressed! Ending...\n");
    sys_end();
}

int main(int argc, char **argv)
{
    int modenum;

    int f;

    /* irq period... */
    WORD period;

    KEY_EVT k;

    SOFT_TASK_MODEL m3, m4, m5, m6;

    PID p3,p4,p5,p6;

    version();

    if (argc == 1)
    {
      cprintf("type x fftplay <freq>");
      return 0;
    }

    f = atoi(argv[1]);
    compute_params(&f,&rawdata_nsamples,&period);

    keyb_set_map(itaMap);
    k.flag = CNTR_BIT;
    k.scan = KEY_C;
    k.ascii = 'c';
    keyb_hook(k,endfun);
    k.flag = CNTL_BIT;
    k.scan = KEY_C;
    k.ascii = 'c';
    keyb_hook(k,endfun);

    cab_windata = cab_create("windata", sizeof(window), 4);
    cab_pwrdata = cab_create("pwr", sizeof(power), 4);

    /* Init the sound lib */
    sound_init((rawdata_nsamples * sizeof(SAMPLE)), NULL);
    sound_info();

    /* Init the data used by the raw_infun */
    init_rawdata();

    /* Start the self-buffering sampling operation */
    sound_setfun(raw_infun, (int (*)(void *))-1);
    sound_sample(NULL, f, 0, DMA_OP | PCM16 | MYFUN, NULL);

    cprintf("Press Enter...");
    while (keyb_getchar() != 13);

    sys_atrunlevel(my_close, NULL, RUNLEVEL_BEFORE_EXIT);

    #if !defined(NO_GRX)
    grx_init();
    modenum = grx_getmode(1024, 768, 16);
    grx_setmode(modenum);

    /* init the graphic mutex */
    sem_init(&mutex, 0, 1);

    /* useful colors ... */
    white = rgb16(255,255,255);
    black = rgb16(0,0,0);
    red   = rgb16(255,0,0);

    scenario(f/2);

    #endif

    #if !defined(NO_GRX)
    soft_task_default_model(m3);
    soft_task_def_period(m3, PERIOD_WAVE);
    soft_task_def_met(m3, WCET_WAVE);
    soft_task_def_group(m3, 1);
    p3 = task_create("wave", wave_task, &m3, NULL);
    if (p3 == -1) {
        perror("FFTPlay: Could not create task <wave>\n");
        sys_end();
    }
    #endif

    soft_task_default_model(m4);
    soft_task_def_period(m4, PERIOD_FFT);
    soft_task_def_met(m4, WCET_FFT);
    soft_task_def_group(m4, 1);
    soft_task_def_stack(m4,32*1024);
    soft_task_def_usemath(m4);
    p4 = task_create("fft", fft_task, &m4, NULL);
    if (p4 == -1) {
        perror("FFTPlay: Could not create task <fft>\n");
        sys_end();
    }

    #if !defined(NO_GRX)
    soft_task_default_model(m5);
    soft_task_def_period(m5, PERIOD_EQU);
    soft_task_def_met(m5, WCET_EQU);
    soft_task_def_group(m5, 1);
    soft_task_def_stack(m5,32*1024);
    soft_task_def_usemath(m5);
    p5 = task_create("equ", equ_task, &m5, NULL);
    if (p5 == -1) {
        perror("FFTPlay: Could not create task <equ>\n");
        sys_end();
    }
    #endif

    #if !defined(NO_GRX)
    soft_task_default_model(m6);
    soft_task_def_period(m6, PERIOD_EQU2D);
    soft_task_def_met(m6, WCET_EQU2D);
    soft_task_def_group(m6, 1);
    soft_task_def_stack(m6,32*1024);
    soft_task_def_usemath(m6);
    p6 = task_create("equ2D", equ2d_task, &m5, NULL);
    if (p6 == -1) {
        perror("FFTPlay: Could not create task <equ2D>\n");
        sys_end();
    }
    #else
    /* Start the prova task */
    //task_def_wcet(m6,1000);
    //task_activate(task_create("prova",prova_task,TASK_TYPE,PERIODIC,200,&m6));
    #endif

    group_activate(1);

    /* Wait until the user get bored */
    while (keyb_getchar() != 13);

    sys_end();

    return 0;
}