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

synth.c

/* adapted for jack and LADCCA by Bob Ham */

/* miniFMsynth 1.0 by Matthias Nagorni    */
/* This program uses callback-based audio */
/* playback as proposed by Paul Davis on  */
/* the linux-audio-dev mailinglist.       */

#define _GNU_SOURCE

#include <stdio.h>   
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <math.h>
#include <jack/jack.h>
#include <alloca.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>

#include <ladcca/ladcca.h>

#define POLY 10

snd_seq_t *seq_handle;
double phi[POLY], phi_mod[POLY], pitch, modulation, velocity[POLY],
       attack, decay, sustain, release, env_time[POLY], env_level[POLY], gain, sample_rate = 48000.0;
int harmonic, subharmonic, transpose, note[POLY], gate[POLY], note_active[POLY];

int process (jack_nframes_t nframes, void * data);
int sample_rate_cb (jack_nframes_t sr, void * data);

cca_client_t * cca_client = NULL;

jack_client_t * jack_client = NULL;
jack_port_t * jack_port = NULL;

char jack_client_name[512];
char alsa_client_name[512];

int quit = 0;

snd_seq_t * open_seq () {
    snd_seq_t *seq_handle;
    
    if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
        fprintf(stderr, "Error opening ALSA sequencer.\n");
        exit(1);
    }
    snd_seq_set_client_name(seq_handle, alsa_client_name);
    if (snd_seq_create_simple_port(seq_handle, "miniFMsynth",
        SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
        SND_SEQ_PORT_TYPE_APPLICATION|SND_SEQ_PORT_TYPE_SYNTH) < 0) {
        fprintf(stderr, "Error creating sequencer port.\n");
        exit(1);
    }

    printf ("Connected to ALSA sequencer with client id %d\n", snd_seq_client_id (seq_handle));
    cca_alsa_client_id (cca_client, (unsigned char) snd_seq_client_id (seq_handle));
    
    return(seq_handle);
}

void
open_jack ()
{
  cca_event_t * event;

  jack_client = jack_client_new (jack_client_name);
  if (!jack_client)
    {
      fprintf (stderr, "%s: could not connect to jack server, exiting\n",
               __FUNCTION__);
      exit (1);
    }
  printf ("Connected to JACK with client name '%s'\n", jack_client_name);

  jack_port = jack_port_register (jack_client, "out_1", JACK_DEFAULT_AUDIO_TYPE,
                                  JackPortIsOutput|JackPortIsTerminal, 0);
  if (!jack_port)
    {
      fprintf (stderr, "%s: could not register jack port, exiting\n",
               __FUNCTION__);
      exit (1);
    }
  
  jack_set_process_callback (jack_client, process, NULL);
  
  sample_rate = jack_get_sample_rate (jack_client);
  jack_set_sample_rate_callback (jack_client, sample_rate_cb, NULL);


  jack_activate (jack_client);

  event = cca_event_new_with_type (CCA_Jack_Client_Name);
  cca_event_set_string (event, jack_client_name);
  cca_send_event (cca_client, event);
  
}

void
close_jack ()
{
  jack_deactivate (jack_client);
  
}

double envelope(int *note_active, int gate, double *env_level, double t, double attack, double decay, double sustain, double release) {

    if (gate)  {
        if (t > attack + decay) return(*env_level = sustain);
        if (t > attack) return(*env_level = 1.0 - (1.0 - sustain) * (t - attack) / decay);
        return(*env_level = t / attack);
    } else {
        if (t > release) {
            if (note_active) *note_active = 0;
            return(*env_level = 0);
        }
        return(*env_level * (1.0 - t / release));
    }
}

int midi_callback() {

    snd_seq_event_t *ev;
    int l1;
  
    do {
        snd_seq_event_input(seq_handle, &ev);
        switch (ev->type) {
            case SND_SEQ_EVENT_PITCHBEND:
                pitch = (double)ev->data.control.value / 8192.0;
                break;
            case SND_SEQ_EVENT_CONTROLLER:
                if (ev->data.control.param == 1) {
                    modulation = (double)ev->data.control.value / 10.0;
                } 
                break;
            case SND_SEQ_EVENT_NOTEON:
                if (ev->data.note.velocity > 0)
                  {
                    for (l1 = 0; l1 < POLY; l1++) {
                        if (!note_active[l1]) {
                            note[l1] = ev->data.note.note;
                            velocity[l1] = ev->data.note.velocity / 127.0;
                            env_time[l1] = 0;
                            gate[l1] = 1;
                            note_active[l1] = 1;
                            break;
                        }
                    }
                    break;
                  }
            case SND_SEQ_EVENT_NOTEOFF:
                for (l1 = 0; l1 < POLY; l1++) {
                    if (gate[l1] && note_active[l1] && (note[l1] == ev->data.note.note)) {
                        env_time[l1] = 0;
                        gate[l1] = 0;
                    }
                }
                break;        
        }
        snd_seq_free_event(ev);
    } while (snd_seq_event_input_pending(seq_handle, 0) > 0);
    return (0);
}

int
sample_rate_cb (jack_nframes_t sr, void * data)
{
  sample_rate = sr;
  
  return 0;
}

void
go_real_time ()
{
  pthread_t jack_thread;
  int policy;
  int set_priority = 0;
  struct sched_param rt_param;
  int err;
  
  memset(&rt_param, 0, sizeof(rt_param));

  jack_thread = jack_client_thread_id (jack_client);
  err = pthread_getschedparam (jack_thread, &policy, &rt_param);
  if (err)
    fprintf (stderr, "%s: could not get scheduling parameters of jack thread\n", __FUNCTION__);
  else
    {
      rt_param.sched_priority += 1;
      set_priority = 1;
    }

  /* make a guess; jackd runs at priority 10 by default I think */
  if (!set_priority)
    {
      memset(&rt_param, 0, sizeof(rt_param));
      rt_param.sched_priority = 15;
    }
  
  policy = SCHED_FIFO;
  
  err = pthread_setschedparam (pthread_self (), policy, &rt_param);
  if (err)
    fprintf (stderr, "%s: could not set SCHED_FIFO for midi thread: %s\n",
             __FUNCTION__, strerror (errno));
  
}

int
process (jack_nframes_t nframes, void * data)
{
    
    int l1, l2;
    double dphi, dphi_mod, f1, f2, f3, freq_note, sound;
    jack_default_audio_sample_t * jack_buffer;
    
    jack_buffer = jack_port_get_buffer (jack_port, nframes);
      
    memset(jack_buffer, 0, nframes * sizeof (jack_default_audio_sample_t));
    for (l2 = 0; l2 < POLY; l2++) {
        if (note_active[l2]) {
            f1 = 8.176 * exp((double)(transpose+note[l2]-2)*log(2.0)/12.0);
            f2 = 8.176 * exp((double)(transpose+note[l2])*log(2.0)/12.0);
            f3 = 8.176 * exp((double)(transpose+note[l2]+2)*log(2.0)/12.0);
            freq_note = (pitch > 0) ? f2 + (f3-f2)*pitch : f2 + (f2-f1)*pitch;
            dphi = M_PI * freq_note / (sample_rate/2);                                    
            dphi_mod = dphi * (double)harmonic / (double)subharmonic;
            for (l1 = 0; l1 < nframes; l1++) {
                phi[l2] += dphi;
                phi_mod[l2] += dphi_mod;
                if (phi[l2] > 2.0 * M_PI) phi[l2] -= 2.0 * M_PI;
                if (phi_mod[l2] > 2.0 * M_PI) phi_mod[l2] -= 2.0 * M_PI;
                sound = gain * envelope(&note_active[l2], gate[l2], &env_level[l2], env_time[l2], attack, decay, sustain, release)
                             * velocity[l2] * sin(phi[l2] +  modulation * sin(phi_mod[l2]));
                env_time[l2] += 1.0 / sample_rate;
                
                
                jack_buffer[l1] += (sound / POLY);
            }
        }    
    }
  return 0;
}

void *      
synth_main (void * data)
{
    int seq_nfds, l1;
    struct pollfd *pfds;
    
    pitch = 0;
    for (l1 = 0; l1 < POLY; note_active[l1++] = 0);

    open_jack ();
    seq_handle = open_seq ();
    
    go_real_time ();

    seq_nfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
    pfds = (struct pollfd *)alloca(sizeof(struct pollfd) * seq_nfds);
    snd_seq_poll_descriptors(seq_handle, pfds, seq_nfds, POLLIN);
    
    printf ("Synth running\n");

    while (!quit) {
        if (poll (pfds, seq_nfds, 1000) > 0) {
            for (l1 = 0; l1 < seq_nfds; l1++) {
               if (pfds[l1].revents > 0) midi_callback();
            }
        }

        if (cca_client && !cca_server_connected (cca_client))
          break;
    }

    snd_seq_close (seq_handle);
    close_jack ();
    
    printf ("Synth finished\n");
    return (NULL);
}


Generated by  Doxygen 1.6.0   Back to index