Logo Search packages:      
Sourcecode: aeolus version File versions  Download package

audio.cc

/*
    Copyright (C) 2003 Fons Adriaensen <fons.adriaensen@skynet.be>
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include <math.h>
#include "audio.h"
#include "messages.h"


extern float exp2ap (float);


Audio::Audio (const char *name) :
    A_thread ("Audio"), 
    _name (name),
    _run_alsa (0),
    _run_jack (0),
    _qnote (256), 
    _qcomm (256), 
    _nasect (0),
    _ndivis (0)
{
}


Audio::~Audio (void)
{
    int i;

    if (_run_alsa) close_alsa ();
    if (_run_jack) close_jack ();
    for (i = 0; i < _nasect; i++) delete _asectp [i];
    for (i = 0; i < _ndivis; i++) delete _divisp [i];
    _reverb.fini ();
}


void Audio::init_audio (void)
{
    M_audio_info  *M;
    int           i;

    M = new M_audio_info ();

    _apar [VOLUME]._val = 0.32f;  
    _apar [VOLUME]._min = 0.00f;
    _apar [VOLUME]._max = 1.00f;
    _apar [REVSIZE]._val = _revsize = 0.075f;
    _apar [REVSIZE]._min =  0.025f;
    _apar [REVSIZE]._max =  0.150f;
    _apar [REVTIME]._val = _revtime = 4.0f;
    _apar [REVTIME]._min =  2.0f;
    _apar [REVTIME]._max =  7.0f;
    _apar [STPOSIT]._val =  0.5f;
    _apar [STPOSIT]._min = -1.0f;
    _apar [STPOSIT]._max =  1.0f;

    _reverb.init (_fsamp);
    _reverb.set_t60mf (_revtime);
    _reverb.set_t60lo (_revtime * 1.50f, 250.0f);
    _reverb.set_t60hi (_revtime * 0.50f, 3e3f);

    for (i = 0; i < NASECT; i++)
    {
        _asectp [i] = new Asection ((float) _fsamp);
        _asectp [i]->set_size (_revsize);
        M->_asectpar [i] = _asectp [i]->get_apar ();
    }

    M->_nasect = _nasect = NASECT;
    M->_fsamp  = _fsamp;
    M->_instrpar = _apar;
    send_event (TO_MODEL, M);
}


void Audio::init_alsa (int ncapt, int nplay, bool bform, const char *device, int fsamp, int fsize, int nfrag)
{
    if (bform) nplay = 4; 
    _run_alsa = true;
    _alsa_handle = new Alsa_driver (device, fsamp, fsize, nfrag, true, false, false);
    if (_alsa_handle->stat () < 0)
    {
        fprintf (stderr, "Error: can't connect to ALSA.\n");
        exit (1);
    } 

    _bform = bform;
    _nplay = _alsa_handle->nplay ();
    _ncapt= 0;
    if (_nplay > nplay) _nplay = nplay;
    if (_nplay > 8) _nplay = 8;
    _fsize = fsize;
    _fsamp = fsamp;

    init_audio ();
    for (int i = 0; i < _nplay; i++) _out [i] = new float [fsize];

    if (thr_start (_policy = SCHED_FIFO, _prioty = -5, 0x00010000))
    {
        fprintf (stderr, "Warning: can't run ALSA thread in RT mode.\n");
        if (thr_start (_policy = SCHED_OTHER, _prioty = 0, 0x00010000))
        {
            fprintf (stderr, "Error: can't create ALSA thread.\n");
            exit (1);
      }
    }

    fprintf (stderr, "Connected to ALSA with %d inputs and %d outputs.\n", _ncapt, _nplay); 
//    _alsa_handle->printinfo ();
}


void Audio::close_alsa ()
{
    fprintf (stderr, "Closing ALSA.\n");
    _run_alsa = false;
    get_event (1 << EV_EXIT);
    for (int i = 0; i < _nplay; i++) delete[] _out [i];
    delete _alsa_handle;
}


void Audio::thr_main (void) 
{
    unsigned long k;

    _alsa_handle->pcm_start ();

    while (_run_alsa)
    {
      k = _alsa_handle->pcm_wait ();  
        proc_comm (&_qnote);
        proc_comm (&_qcomm);
        proc_update ();
        while (k >= _fsize)
            {
            proc_synth (_fsize);
            _alsa_handle->play_init (_fsize);
            for (int i = 0; i < _nplay; i++) _alsa_handle->play_chan (i, _out [i], _fsize);
            _alsa_handle->play_done (_fsize);
            k -= _fsize;
      }
        proc_mesg ();
    }

    _alsa_handle->pcm_stop ();
    put_event (EV_EXIT);
}


void Audio::init_jack (int ncapt, int nplay, bool bform)
{
    if (bform) nplay = 4; 
    struct sched_param params;
    char s [16];
    
    _run_jack = true;

    _bform = bform;
    _nplay = nplay;
    _ncapt = ncapt;
    if (_ncapt > 0) _ncapt = 0;
    if (_nplay > 8) _nplay = 8;

    if ((_jack_handle = jack_client_new (_name)) == 0)
    {
        fprintf (stderr, "Error: can't connect to JACK.\n");
        exit (1);
    }

    jack_set_process_callback (_jack_handle, jack_static_callback, (void *)this);
    jack_on_shutdown (_jack_handle, jack_static_shutdown, (void *)this);

    for (int i = 0; i < _nplay; i++)
    {
        sprintf(s, "out_%d", i + 1);
        _jack_out [i] = jack_port_register (_jack_handle, s, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
    }
    for (int i = 0; i < _ncapt; i++)
    {
        sprintf(s, "in_%d", i + 1);
        _jack_in [i] = jack_port_register (_jack_handle, s, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
    }

    _fsamp = jack_get_sample_rate (_jack_handle);
    init_audio ();

    if (jack_activate (_jack_handle))
    {
        fprintf(stderr, "Error: can't activate JACK.");
        exit (1);
    }

    pthread_getschedparam (jack_client_thread_id (_jack_handle), &_policy, &params);
    _prioty = params.sched_priority -  sched_get_priority_max (_policy);
    fprintf (stderr, "Connected to JACK with %d inputs and %d outputs.\n", _ncapt, _nplay); 
}


void Audio::close_jack ()
{
    fprintf (stderr, "Closing JACK.\n");
    jack_deactivate (_jack_handle);
    for (int i = 0; i < _nplay; i++) jack_port_unregister(_jack_handle, _jack_out[i]);
    for (int i = 0; i < _ncapt; i++) jack_port_unregister(_jack_handle, _jack_in[i]);
    jack_client_close (_jack_handle);
}


void Audio::jack_static_shutdown (void *arg)
{
    return ((Audio *) arg)->jack_shutdown ();
}


void Audio::jack_shutdown (void)
{
    _run_jack = false;
    send_event (EV_EXIT, 1);
}


int Audio::jack_static_callback (jack_nframes_t nframes, void *arg)
{
    return ((Audio *) arg)->jack_callback (nframes);
}


int Audio::jack_callback (jack_nframes_t nframes)
{
    for (int i = 0; i < _nplay; i++) _out [i] = (float *)(jack_port_get_buffer (_jack_out [i], nframes));
    proc_comm (&_qnote);
    proc_comm (&_qcomm);
    proc_update ();
    proc_synth (nframes);
    proc_mesg ();
    return 0;
}


void Audio::proc_comm (Lfq_u32 *Q) 
{
    U32   k;
    int   c, i, j, b;
    float v;

    while (Q->rd_avail ())
    {
        k = *(Q->rd_ptr ());
        Q->rd_commit ();        
        c = k >> 24;      
        j = (k >> 16) & 255;
        i = (k >>  8) & 255; 
        b = k & 255;

        switch (c)
      {
      case 0:
            note_off (i, b);
          break;

      case 1:
            note_on (i, b);
          break;

      case 2:
            for (i = 0; i < NNOTES; i++) if (_keys [i] & j) note_off (i, b);
          break;

      case 3:
          for (i = 0; i < NNOTES; i++) if (_keys [i] & j) note_on (i, b);
          break;

        case 4:
            _divisp [j]->clr_div_mask (b); 
            break;

        case 5:
            _divisp [j]->set_div_mask (b); 
            break;

        case 6:
            _divisp [j]->clr_rank_mask (i, b); 
            break;

        case 7:
            _divisp [j]->set_rank_mask (i, b); 
            break;

        case 16:
            if (b) _divisp [j]->trem_on (); 
            else   _divisp [j]->trem_off ();
            break;

        case 17:
            v = *((float *)(Q->rd_ptr ()));
            Q->rd_commit ();        
            switch (i)
          {
            case 0: _divisp [j]->set_swell (v); break;
            case 1: _divisp [j]->set_tfreq (v); break;
            case 2: _divisp [j]->set_tmodd (v); break;
            break;
          }
      }
    }
}


void Audio::proc_update (void)
{    
    int d, m, n;

    for (n = 0; n < NNOTES; n++)
    {
      m = _keys [n];
      if (m & 128)
      {
            m &= 127;
          _keys [n] = m;
            for (d = 0; d < _ndivis; d++) _divisp [d]->update (n, m);
      }
    }
    for (d = 0; d < _ndivis; d++) _divisp [d]->update (_keys);
}


void Audio::proc_synth (unsigned long len) 
{
    int           j;
    float         W [PERIOD];
    float         X [PERIOD];
    float         Y [PERIOD];
    float         Z [PERIOD];
    float         R [PERIOD];
    float        *out [8];

    if (fabsf (_revsize - _apar [REVSIZE]._val) > 0.001f)
    {
        _revsize = _apar [REVSIZE]._val;
      _reverb.set_delay (_revsize);
        for (j = 0; j < _nasect; j++) _asectp[j]->set_size (_revsize);
    }
    if (fabsf (_revtime - _apar [REVTIME]._val) > 0.1f)
    {
        _revtime = _apar [REVTIME]._val;
      _reverb.set_t60mf (_revtime);
      _reverb.set_t60lo (_revtime * 1.50f, 250.0f);
      _reverb.set_t60hi (_revtime * 0.50f, 3e3f);
    }

    for (j = 0; j < _nplay; j++) out [j] = _out [j];

    while (len >= PERIOD)
    {
        len -= PERIOD;

        memset (W, 0, PERIOD * sizeof (float));
        memset (X, 0, PERIOD * sizeof (float));
        memset (Y, 0, PERIOD * sizeof (float));
        memset (Z, 0, PERIOD * sizeof (float));
        memset (R, 0, PERIOD * sizeof (float));

        for (j = 0; j < _ndivis; j++) _divisp [j]->process ();
        for (j = 0; j < _nasect; j++) _asectp [j]->process (_apar [VOLUME]._val, W, X, Y, R);
        _reverb.process (PERIOD, _apar [VOLUME]._val, R, W, X, Y, Z);

        if (_bform)
      {
            for (j = 0; j < PERIOD; j++)
            {
              out [0][j] = W [j];
              out [1][j] = 1.41 * X [j];
              out [2][j] = 1.41 * Y [j];
              out [3][j] = 1.41 * Z [j];
          }
      }
        else if (_nplay < 4)  
        {
            for (j = 0; j < PERIOD; j++)
            {
              out [0][j] = W [j] + _apar [STPOSIT]._val * X [j] + Y [j];
              out [1][j] = W [j] + _apar [STPOSIT]._val * X [j] - Y [j];
          }
      }
        else if (_nplay < 8)
      {
            for (j = 0; j < PERIOD; j++)
            {
              out [0][j] = W [j] + X [j] + Y [j];
              out [1][j] = W [j] + X [j] - Y [j];
              out [2][j] = W [j] - X [j] - Y [j];
              out [3][j] = W [j] - X [j] + Y [j];
          }
      }
      else
      {
            for (j = 0; j < PERIOD; j++)
            {
              out [0][j] = W [j] + X [j] + Y [j] - Z [j];
              out [1][j] = W [j] + X [j] - Y [j] - Z [j];
              out [2][j] = W [j] - X [j] - Y [j] - Z [j];
              out [3][j] = W [j] - X [j] + Y [j] - Z [j];
              out [4][j] = W [j] + X [j] + Y [j] + Z [j];
              out [5][j] = W [j] + X [j] - Y [j] + Z [j];
              out [6][j] = W [j] - X [j] - Y [j] + Z [j];
              out [7][j] = W [j] - X [j] + Y [j] + Z [j];
          }
      }

      for (j = 0; j < _nplay; j++) out [j] += PERIOD;
    }
}


void Audio::proc_mesg (void) 
{
    ITC_mesg *M;

    while (get_event_nowait () != EV_TIME)
    {
      M = get_message ();
        if (! M) continue; 

        switch (M->type ())
      {
          case MT_NEW_DIVIS:
          {
              M_new_divis  *X = (M_new_divis *) M;
                Division     *D = new Division (_asectp [X->_asect], (float) _fsamp);
                D->set_div_mask (X->_dmask);
                D->set_swell (X->_swell);
                D->set_tfreq (X->_tfreq);
                D->set_tmodd (X->_tmodd);
                _divisp [_ndivis] = D;
                _ndivis++;
                break; 
          }
          case MT_CALC_RANK:
          case MT_LOAD_RANK:
          {
              M_def_rank *X = (M_def_rank *) M;
                _divisp [X->_divis]->set_rank (X->_rank, X->_wave,  X->_sdef->_pan, X->_sdef->_del);  
                send_event (TO_MODEL, M);
                M = 0;
              break;
          }
          case MT_AUDIO_SYNC:
                send_event (TO_MODEL, M);
                M = 0;
            break;
      } 
        if (M) M->recover ();
    }
}


Generated by  Doxygen 1.6.0   Back to index