Logo Search packages:      
Sourcecode: jackbeat version File versions

sequence.c

/*
 *   Jackbeat - Audio Sequencer
 *
 *   Copyright (c) 2004-2006 Olivier Guilyardi <olivier {at} samalyse {dot} com>
 *    
 *   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.
 *
 *   SVN:$Id: sequence.c 64 2007-02-28 17:27:46Z olivier $
 */

#define SEQUENCE_MASK_ATTACK_DELAY 3.0 // Miliseconds

#include <jack/jack.h>
#include <samplerate.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <assert.h>
#include <math.h>
#include <unistd.h>
#include <config.h>

#include "sequence.h"
#include "error.h"
#include "msg.h"

#ifdef MEMDEBUG
#include "memdebug.h"
#endif

#ifdef DMALLOC
#include "dmalloc.h"
#endif


/********************************
 *    Main data structures      *
 ********************************/

/* SEQUENCE-------------------------------------+
   |                                            |
   | TRACK[0]---------------+       TRACK[n]-+  |
   | |                      |       |        |  |
   | | > frame position     |       |        |  |
   | | > pattern ("beats")  |       |        |  |
   | |                      |       |        |  |
   | | SAMPLE-------------+ |       |        |  |
   | | | (see sample.h)   | |       |        |  |
   | | +------------------+ |       |        |  |
   | |                      | [...] |        |  |
   | | CHANNEL[0]---------+ |       |        |  |
   | | | > jack port      | |       |        |  |
   | | | > jack buffer    | |       |        |  |
   | | +------------------+ |       |        |  |
   | |                      |       |        |  |
   | |       [...]          |       |        |  |
   | |                      |       |        |  |
   | | CHANNEL[n]---------+ |       |        |  |
   | | | > jack port      | |       |        |  |
   | | | > jack buffer    | |       |        |  |
   | | +------------------+ |       |        |  |
   | |                      |       |        |  |
   | +----------------------+       +--------+  |
   |                                            |
   +--------------------------------------------+
*/

typedef struct sequence_track_t
{
  char *          beats;
  char *          mask;

  sample_t *      sample;

  jack_port_t **  channels;
  int             channels_num;
  jack_nframes_t *frame_pos;
  float **        buffers;
  jack_nframes_t  buffers_ofs;

  int             active_beat;
  int             active_mask_beat;
  char            name[256];

  char            enabled;
  char            lock;
  SRC_STATE *     sr_converter;
  int             sr_converter_type;
  float *         sr_converter_buffer;
  double          sr_converter_ratio;
  double          pitch;
  double          volume;
  double          mask_envelope;
  int             smoothing;
  float           current_level;
  float           level_peak;
} sequence_track_t;


typedef enum sequence_status_t
{
  SEQUENCE_DISABLED,
  SEQUENCE_ENABLED,
} sequence_status_t;

struct sequence_t
{
  sequence_track_t *tracks;
  int               tracks_num;
  int               beats_num;
  int               measure_len;
  float             bpm;
  sequence_status_t status;
  int               looping;
  int               transport_aware;
  int               transport_query;
  jack_nframes_t    transport_pos_buf;
  jack_client_t *   client;
  jack_nframes_t    framerate;
  jack_nframes_t    buffer_size;
  char              name[32];
  int               auto_connect;
  int               sr_converter_default_type;
  msg_t *           msg;

};

/****************************************************************************
 * Type and macros for messages sent from the main loop to the audio thread *
 ****************************************************************************/

typedef struct sequence_msg_t
{
  long int type;
  char text[128];
} sequence_msg_t;

#define SEQUENCE_MSG_SIZE 128

#define SEQUENCE_MSG_SET_BPM 2
#define SEQUENCE_MSG_SET_TRANSPORT 4
#define SEQUENCE_MSG_SET_LOOPING 5
#define SEQUENCE_MSG_UNSET_LOOPING 6
#define SEQUENCE_MSG_START 7
#define SEQUENCE_MSG_STOP 8
#define SEQUENCE_MSG_REWIND 9
#define SEQUENCE_MSG_SET_SR_RATIO 10
#define SEQUENCE_MSG_SET_VOLUME 11
#define SEQUENCE_MSG_SET_SMOOTHING 12

#define SEQUENCE_MSG_NO_ACK -32

#define SEQUENCE_MSG_ACK 33

#define SEQUENCE_MSG_LOCK_TRACKS 34
#define SEQUENCE_MSG_UNLOCK_TRACKS 35
#define SEQUENCE_MSG_LOCK_SINGLE_TRACK 36
#define SEQUENCE_MSG_UNLOCK_SINGLE_TRACK 37
#define SEQUENCE_MSG_RESIZE 38
#define SEQUENCE_MSG_SET_SAMPLE 39
#define SEQUENCE_MSG_MUTE_TRACK 40
#define SEQUENCE_MSG_ENABLE_MASK 41
#define SEQUENCE_MSG_DISABLE_MASK 42
#define SEQUENCE_MSG_ACK_STOP 43
#define SEQUENCE_MSG_ACK_ENABLE 44
#define SEQUENCE_MSG_ACK_DISABLE 45


/*******************************
 *   Various type and macros   *
 *******************************/

#define SEQUENCE_NESTED 1
#define SEQUENCE_SILENT 2

typedef struct sequence_resize_data_t
{
  sequence_track_t *tracks;
  int tracks_num;
  int beats_num;
  int measure_len;
} sequence_resize_data_t;

int sequence_index = 0;

#define DEBUG(M, ...) { printf("SEQ %s  %s(): ",sequence->name, __func__); printf(M, ## __VA_ARGS__); printf("\n"); }
#define VDEBUG(M, ...) 

int dump_i;
char *dump_s;
#define DUMP(V,S) \
  { \
   dump_s = (char *)V; \
   for (dump_i=0; dump_i < S; dump_i++) printf("%.2X ", dump_s[dump_i]); \
   printf ("\n"); \
  }


/******************************************************
 *   Private functions running inside JACK's thread   *
 ******************************************************/

/**
 * Initialize jack audio data buffers for a given track. 
 */
static void
sequence_get_buffers (sequence_t * sequence, int track,
                      jack_nframes_t nframes)
{
  int j;
  sequence_track_t *t = sequence->tracks + track;
  for (j = 0; j < t->channels_num; j++)
    t->buffers[j] = (float *) jack_port_get_buffer (t->channels[j], nframes);
}

/**
 * Initialize jack audio data buffers for all but locked tracks.
 */
static void 
sequence_get_all_buffers (sequence_t * sequence, jack_nframes_t nframes)
{
  
  int i;
  for (i=0; i < sequence->tracks_num; i++)
    if ((sequence->tracks+i)->channels_num && !(sequence->tracks+i)->lock)
      sequence_get_buffers (sequence, i, nframes);
}

/**
 * Copies sample data to output buffers.
 *
 * Copies nframes frames from a given track's sample to the corresponding jack
 * audio buffers, and increases the internal track pointer (frame_pos)
 * accordingly. If there's not enough sample data, this function will zero-fill
 * the remaining space in the jack buffers
 */
static int
sequence_copy_sample_data (sequence_t * sequence, int track,
                           jack_nframes_t nframes, char mask,
                           long unsigned int offset_next,
                           float *max_level)
{
  int j, k;
  //char ret = 1;
  SRC_DATA src_data;
  int src_error;
  float *filtered_data;
  jack_nframes_t nframes_avail;
  jack_nframes_t nframes_used;
  jack_nframes_t nframes_filtered;
  jack_nframes_t nframes_required;

  sequence_track_t *t = sequence->tracks + track;

  // What we have to produce and what is available
  nframes_required = nframes;
  nframes_avail = (*(t->frame_pos) < t->sample->frames)
    ? t->sample->frames - *(t->frame_pos) : 0;

  // Performing sample rate conversion
  if (t->sr_converter_type == SEQUENCE_SINC)
   {
    if (*(t->frame_pos) == 0) src_reset (t->sr_converter);
    src_data.data_in = t->sample->data + *(t->frame_pos) * t->channels_num;
    filtered_data = src_data.data_out = t->sr_converter_buffer;
    src_data.input_frames = nframes_avail;
    src_data.output_frames = nframes_required;
    src_data.src_ratio = t->sr_converter_ratio;
    src_data.end_of_input = nframes_avail ? 0 : 1;
    src_error = src_process (t->sr_converter, &src_data);
    if (src_error)
     {
      DEBUG ("FATAL: Failed to convert the sample rate : %s", 
             src_strerror (src_error));
      exit (1);
     }
    nframes_filtered = src_data.output_frames_gen;
    nframes_used = src_data.input_frames_used;
   }
  else
   {
    for (j=0, k=0; (j < nframes_required) && (k < nframes_avail); j++)
     {
      memcpy (t->sr_converter_buffer + j * t->channels_num,
              t->sample->data + (*(t->frame_pos) + k) * t->channels_num,
              t->channels_num * sizeof (float));
      k = (double) j / t->sr_converter_ratio;
     }
    nframes_filtered = j;
    nframes_used = k;
    filtered_data = t->sr_converter_buffer;
   }

  double mask_env_interval = ((double) sequence->framerate * ((double) SEQUENCE_MASK_ATTACK_DELAY / 1000));
  double mask_env_delta = (double) 1 / mask_env_interval;
  double mask_env = t->mask_envelope;  
 
  // Filling jack buffers
  float level;
  for (j = 0; j < t->channels_num; j++)
    {
      mask_env = t->mask_envelope;
      for (k = 0 ; k < nframes_filtered ; k++)
       {
        if (mask && ((offset_next == 0) || (offset_next - k >
                                            mask_env_interval)))
         {
          if ((*(t->frame_pos) == 0)) mask_env = 1;
          else if (mask_env < 1)
           {
            mask_env += mask_env_delta;
            if (mask_env > 1) mask_env = 1;
           }
         }
        else
         {
          if ((*(t->frame_pos) == 0)) mask_env = 0;
          else if (mask_env > 0) 
           {
            mask_env -= mask_env_delta;
            if (mask_env < 0) mask_env = 0;
           }
         }
         
        t->buffers[j][k + t->buffers_ofs] = filtered_data[k * t->channels_num + j] * t->volume * mask_env;
        level = fabsf (filtered_data[k * t->channels_num + j] * mask_env);
        if (level > (*max_level)) (*max_level) = level;
       }
      for (k = nframes_filtered; k < nframes_required; k++) t->buffers[j][k + t->buffers_ofs] = 0;

    }

  t->buffers_ofs += nframes_required; 
  t->mask_envelope = mask_env;

  // Increasing this track's frame counter
  *(t->frame_pos) += nframes_used;

  // Returning the number of frames in the output buffers that really got
  // filled with sample data (versus: zero-filled)
  return nframes_filtered;
}

/** 
 * zero-fills jack buffers with a length of n frames, for a given track. 
 */
static void
sequence_zero_fill (sequence_t * sequence, int track, jack_nframes_t nframes)
{
  int j, k;
  sequence_track_t *t = sequence->tracks + track;

  for (j = 0; j < t->channels_num; j++)
    for (k = 0; k < nframes; k++) t->buffers[j][k] = 0;

  t->buffers_ofs += nframes; 
}

/**
 * Receive IPC messages.
 */
static void
sequence_receive_messages (sequence_t * sequence)
{
  sequence_msg_t msg;
  sequence_track_t *t;
  int i,j;
  sample_t *samq;
  float f;
  int *tl, st;
  char *mask;

  while (msg_receive (sequence->msg, &msg))
  {
    switch (msg.type)
    {
      case SEQUENCE_MSG_RESIZE:
        sscanf (msg.text, "tracks=%p tracks_num=%d beats_num=%d measure_len=%d",
                &(sequence->tracks), 
                &(sequence->tracks_num),
                &(sequence->beats_num),
                &(sequence->measure_len));
        break;
      case SEQUENCE_MSG_LOCK_TRACKS:
        sscanf (msg.text, "tracks=%p", &tl);
        while (*tl != -1)
          sequence->tracks[*(tl++)].lock = 1;
        DEBUG ("Tracks locks data received and stored")
        break;
      case SEQUENCE_MSG_UNLOCK_TRACKS:
        sscanf (msg.text, "tracks=%p", &tl);
        while (*tl != -1)
          sequence->tracks[*(tl++)].lock = 0;
        break;
      case SEQUENCE_MSG_LOCK_SINGLE_TRACK:
        sscanf (msg.text, "track=%d", &st);
        sequence->tracks[st].lock = 1;
        break;
      case SEQUENCE_MSG_UNLOCK_SINGLE_TRACK:
        sscanf (msg.text, "track=%d", &st);
        sequence->tracks[st].lock = 0;
        break;
      case SEQUENCE_MSG_SET_SAMPLE:
        sscanf (msg.text, "track=%d sample=%p", &i, &samq); 
        t = sequence->tracks + i;
        t->sample = samq;
        *(t->frame_pos) = t->sample->frames;
        t->lock = 0;
        break;
      case SEQUENCE_MSG_MUTE_TRACK:
        sscanf (msg.text, "track=%d mute=%d", &i, &j);
        sequence->tracks[i].enabled = j;
        break;
      case SEQUENCE_MSG_ENABLE_MASK:
        sscanf (msg.text, "mask=%p track=%d", &mask, &i);
        sequence->tracks[i].mask = mask;
        break;
      case SEQUENCE_MSG_DISABLE_MASK:
        sscanf (msg.text, "track=%d", &i);
        sequence->tracks[i].mask = NULL;
        break;
      case SEQUENCE_MSG_ACK_STOP:
        sequence->status = SEQUENCE_DISABLED;
        if (sequence->transport_query) jack_transport_stop (sequence->client);
        break;
      case SEQUENCE_MSG_ACK_ENABLE:
        sequence->status = SEQUENCE_ENABLED;
        break;
      case SEQUENCE_MSG_ACK_DISABLE:
        sequence->status = SEQUENCE_DISABLED;
        break;
      case SEQUENCE_MSG_SET_BPM:
        sscanf (msg.text, "bpm=%f", &(sequence->bpm));
        DEBUG ("Setting bpm to %f", sequence->bpm);
        break;
      case SEQUENCE_MSG_SET_TRANSPORT:
        sscanf (msg.text, "aware=%d query=%d",
                &(sequence->transport_aware),
                &(sequence->transport_query));
        break;
      case SEQUENCE_MSG_SET_LOOPING:
        sequence->looping = 1;
        break;
      case SEQUENCE_MSG_UNSET_LOOPING:
        sequence->looping = 0;
        break;
      case SEQUENCE_MSG_START:
        sequence->status = SEQUENCE_ENABLED;
        if (sequence->transport_query)
          jack_transport_start (sequence->client);
        break;
      case SEQUENCE_MSG_STOP:
        sequence->status = SEQUENCE_DISABLED;
        if (sequence->transport_query)
          jack_transport_stop (sequence->client);
        break;
      case SEQUENCE_MSG_REWIND:
        jack_transport_locate (sequence->client, 0);
        break;
      case SEQUENCE_MSG_SET_SR_RATIO:
        sscanf (msg.text, "track=%d ratio=%f", &i, &f);
        t = sequence->tracks + i;
        t->sr_converter_ratio = f;
        break;
      case SEQUENCE_MSG_SET_VOLUME:
        sscanf (msg.text, "track=%d volume=%f", &i, &f);
        sequence->tracks[i].volume = f;
        break;
      case SEQUENCE_MSG_SET_SMOOTHING:
        sscanf (msg.text, "track=%d status=%d", &i, &j);
        sequence->tracks[i].smoothing = j;
        break;
      }

      msg_sync(sequence->msg);
  }

}

/**
 * Compute the duration (number of frames) of a beat. 
 */
static jack_nframes_t
sequence_get_beat_nframes (sequence_t *sequence)
{
  return 60 * sequence->framerate / sequence->bpm / sequence->measure_len;
}

/**
 * Perform sequencing.
 */
static jack_nframes_t
sequence_do_process (sequence_t *sequence, jack_nframes_t position, jack_nframes_t nframes)
{
  int i;
  int current_beat;
  int previous_beat;
  int current_offset;
  jack_nframes_t beat_nframes; // should be a double ?
  jack_nframes_t footer = 0;
  int beat_trigger = 0;
 
  beat_nframes = sequence_get_beat_nframes (sequence);

  current_beat = position / beat_nframes;
  current_offset = position % beat_nframes;

  if (sequence->looping) 
   {
    if (current_offset == 0) beat_trigger = 1;
    else if (current_offset + nframes > beat_nframes)
      {
        current_beat++;
        footer = beat_nframes - current_offset;
        current_offset = 0;
        beat_trigger = 1;
      }
    
    if (current_beat >= sequence->beats_num)
      current_beat = current_beat % sequence->beats_num;
   }
  else if (current_beat < sequence->beats_num && current_offset == 0)
   {
      beat_trigger = 1;
   }
  else if (current_offset + nframes > beat_nframes && current_beat <
           (sequence->beats_num - 1))
    {
      current_beat++;
      footer = beat_nframes - current_offset;
      current_offset = 0;
      beat_trigger = 1;
    }

  sequence_track_t *t;
  char mask;
  jack_nframes_t nframes_played = 0;
  jack_nframes_t nframes_copied, n;
  for (i = 0; i < sequence->tracks_num; i++)
    {
      t = sequence->tracks + i;
      if (!t->lock)
        {
          t->current_level = 0;
          if (t->sample != NULL)
            {
              t->buffers_ofs = 0;
              nframes_copied = 0;
              if (beat_trigger)
                {
                  previous_beat = (current_beat > 0) 
                   ? current_beat - 1 : sequence->beats_num - 1;
                  mask = (t->mask) ? t->mask[previous_beat] : 1;
                  nframes_copied = sequence_copy_sample_data (sequence, i, footer, 
                                    mask & t->enabled, footer, &(t->current_level)); 

                  if (t->beats[current_beat])
                    {
                      *(t->frame_pos) = 0;
                      t->active_beat = current_beat;
                      
                    }
                }
             
              // Looking up next active beat
              int dist;
              if ((!t->smoothing) || t->active_beat == -1) dist = -1;
              else
               {
                for (dist = 1; 
                     (current_beat + dist < sequence->beats_num) 
                      && (!t->beats[current_beat + dist]);
                     dist++)
                  ;
                if (current_beat + dist == sequence->beats_num)
                 {
                  if (!sequence->looping) dist = -1;
                  else
                   {
                    for ( ; (dist <= sequence->beats_num) 
                              && (!t->beats[current_beat + dist - sequence->beats_num]);
                          dist++)
                      ;
                    if (!t->beats[current_beat + dist - sequence->beats_num])
                      dist = -1;
                   }
                 }
               }
              
              long unsigned int offset_next = 0;
              if (dist != -1)
               {
                offset_next = beat_nframes - current_offset + (dist - 1) * beat_nframes; 
                
               }
                   
              if ((t->active_beat != -1) && (!t->beats[t->active_beat])) 
               {
                *(t->frame_pos) = t->sample->frames;
                // FIXME: What if there is no sr_converter because we're using the 
                // SEQUENCE_LINEAR one ?
                if (t->sr_converter != NULL) src_reset (t->sr_converter);
               }
              
              mask = (t->mask != NULL) ? t->mask[current_beat] : 1;

              n = sequence_copy_sample_data (sequence, i, nframes - footer, 
                    mask & t->enabled, offset_next, &(t->current_level));
              
              if (!n) t->active_beat = -1;

              nframes_copied += n;
              if (nframes_copied > nframes_played) nframes_played = nframes_copied;

              t->active_mask_beat = ((t->active_beat != -1) && t->mask && mask
                                     && (current_beat < sequence->beats_num)) ? current_beat : -1;
            }
          else
            sequence_zero_fill (sequence, i, nframes);
        }
    }

  return nframes_played;
}

/**
 * Process a given number of frames
 *
 * This is the JACK callback function.
 */
static int
sequence_process (jack_nframes_t nframes, void *data)
{
  int i;
  jack_position_t info;
  sequence_t *sequence = (sequence_t *) data;
  sequence_receive_messages (sequence);

  if (!sequence->tracks_num) return 0;
  
  sequence_get_all_buffers (sequence, nframes);

  if ((jack_transport_query (sequence->client,
                            &info) == JackTransportRolling)
      && sequence->status == SEQUENCE_ENABLED)
   {
    sequence_do_process (sequence, info.frame, nframes);
   }
  else
   {
     for (i=0; i < sequence->tracks_num; i++)
       if ((sequence->tracks+i)->channels_num && !(sequence->tracks+i)->lock) 
        {
          sequence_zero_fill (sequence, i, nframes);
        }
   }
  return 0;
}

/******************************************************
 *   Private functions running inside the main loop   *
 *   (these are called by the public functions)       *
 ******************************************************/

static void
sequence_init (sequence_t * sequence)
{
  sequence->tracks = NULL;
  sequence->tracks_num = 0;
  sequence->beats_num = 0;
  sequence->measure_len = 0;
  sequence->bpm = 100;
  sequence->status = SEQUENCE_DISABLED;
  sequence->looping = 1;
  sequence->transport_aware = 1;
  sequence->transport_query = 1;
  sequence->transport_pos_buf = 0;
  sequence->client = NULL;
  sequence->framerate = 0;
  sequence->name[0] = '\0';
  sequence->auto_connect = 1;
  sequence->msg = NULL;
  sequence->sr_converter_default_type = SEQUENCE_LINEAR;
}

static void
sequence_track_init (sequence_track_t *track)
{
  track->beats = NULL;
  track->mask = NULL;
  track->sample = NULL;
  track->channels = NULL;
  track->channels_num = 1;
  track->frame_pos = NULL;
  track->buffers = NULL;
  track->active_beat = -1;
  track->active_mask_beat = -1;
  track->current_level = 0;
  track->name[0] = '\0';
  track->enabled = 1;
  track->lock = 0;
  track->sr_converter = NULL;
  track->sr_converter_type = -1;
  track->sr_converter_buffer = NULL;
  track->sr_converter_ratio = 1;
  track->pitch = 0;
  track->volume = 1;
  track->mask_envelope = 1;
  track->smoothing = 1;
  track->level_peak = 1;
}

static void
sequence_connect_playback (sequence_t * sequence, sequence_track_t *track)
{
  const char **ports;
  int i;
  const char **c = NULL;

  if ((ports = jack_get_ports (sequence->client, NULL, NULL,
                               JackPortIsPhysical | JackPortIsInput)))
    {
      for (i = 0;
           i < 2 && i < track->channels_num && ports[i]
           && !(c =
                jack_port_get_connections (track->channels[i])); 
           i++);
      if (c)
        {
          free (c);
          return;
        }
      for (i = 0;
           i < 2 && i < track->channels_num && ports[i]; i++)
        {
          jack_connect (sequence->client,
                        jack_port_name (track->channels[i]),
                        ports[i]);
        }
      // If this is a mono track, also connect it to the right channel
      if (track->channels_num == 1 && ports[1])
      {
        jack_connect (sequence->client,
                      jack_port_name (track->channels[0]),
                      ports[1]);
      }
      free (ports);
    }

}

static void
sequence_destroy_track (sequence_t *sequence, int track, int destroy_beats)
{
  sequence_track_t *t = sequence->tracks + track;

  int j;
  for (j = 0; j < t->channels_num; j++)
    jack_port_unregister (sequence->client, t->channels[j]);
  free (t->frame_pos);
  free (t->channels);
  free (t->buffers);
  if (t->sr_converter != NULL)
   {
    src_delete (t->sr_converter);
    t->sr_converter = NULL;
    free (t->sr_converter_buffer);
    t->sr_converter_buffer = NULL;
   }

  if (destroy_beats) 
   {
      free (t->beats);
      if (t->mask) free (t->mask);
   }
}

static char * 
sequence_make_port_name (sequence_track_t *track, int channel)
{
  char *s = malloc (128);

  switch (track->channels_num)
   {
    case 1: 
      strcpy (s, track->name); 
      break;
    case 2: 
      if (channel == 0) sprintf (s, "%s_L", track->name);
      else sprintf (s, "%s_R", track->name);
      break;
    default: 
      sprintf (s, "%s_%d", track->name, channel + 1); 
      break;
   }

  return s;
}

static void
sequence_register_track (sequence_t *sequence, sequence_track_t *track)
{
  int j;

  DEBUG("Channels: %d", track->channels_num);
  for (j = 0; j < track->channels_num; j++) {
    char *name = sequence_make_port_name (track, j);
    track->channels[j] = jack_port_register (sequence->client, name, JACK_DEFAULT_AUDIO_TYPE, 
                                             JackPortIsOutput, 0);
    free(name);
  }
}

/*********************************
 *       Public functions        *
 *********************************/

sequence_t *
sequence_new (char *name, int *error) 
{
  sequence_t *sequence;

  sequence = calloc (1, sizeof (sequence_t));
  DEBUG ("index: %d", sequence_index); 
  sequence_init (sequence);
  DEBUG ("name : %s", name);
  strcpy (sequence->name, name);

  sequence->msg = msg_new (4096, sizeof(sequence_msg_t));

  /* Creating JACK client */
  DEBUG ("Initializing JACK");
  if ((sequence->client = jack_client_new (sequence->name)) == 0)
    {
      fprintf (stderr, "jack server not running?\n");
      sequence_destroy (sequence);
      *error = ERR_SEQUENCE_JACK_CONNECT;
      return NULL;
    }

  jack_set_process_callback (sequence->client, sequence_process,
                             (void *) sequence);

  sequence->buffer_size = jack_get_buffer_size (sequence->client);
  
  if (jack_activate (sequence->client))
    {
      fprintf (stderr, "cannot activate client\n");
      sequence_destroy (sequence);
      *error = ERR_SEQUENCE_JACK_ACTIVATE;
      return NULL;
    }
  sequence->framerate = jack_get_sample_rate (sequence->client);
  DEBUG ("Jack Framerate : %d", sequence->framerate);

  sequence_resize (sequence, 4, 16, 4, 0);
  
  return sequence;
}

void
sequence_rewind (sequence_t * sequence)
{
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_REWIND;
  msg.text[0] = '\0';
  msg_send (sequence->msg, &msg, 0);
}

void
sequence_start (sequence_t * sequence)
{
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_START;
  msg.text[0] = '\0';
  msg_send (sequence->msg, &msg, 0);
}

void
sequence_stop (sequence_t * sequence)
{
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_STOP;
  msg.text[0] = '\0';
  msg_send (sequence->msg, &msg, 0);
}

void
sequence_destroy (sequence_t * sequence)
{
  int i;
  sequence_track_t *t;
  if (sequence->client != NULL)
   {
    jack_deactivate (sequence->client);
    jack_client_close (sequence->client);
   }
  // FIXME: May need to sync in here
  msg_destroy (sequence->msg);
  for (i=0; i < sequence->tracks_num; i++)
   {
    t = sequence->tracks + i;
    free (t->beats);
    if (t->mask) free (t->mask);
    free (t->channels);
    free (t->frame_pos);
    free (t->buffers);
   }
  if (sequence->tracks != NULL) free (sequence->tracks);
  free (sequence);
}

void
sequence_resize (sequence_t * sequence, int tracks_num, int beats_num,
                 int measure_len, int duplicate_beats)
{
  int i, j;
  sequence_msg_t msg;
  
  /* Asks the audio thread to avoid playing (lock) tracks whose jack ports are
     about to be unregistered */
  if (tracks_num < sequence->tracks_num)
    {
      int *tl = calloc (sequence->tracks_num - tracks_num + 1, sizeof (int));
      for (i = tracks_num, j = 0; i < sequence->tracks_num; i++)
        tl[j++] = i;
      tl[j] = -1;
      DEBUG("Requiring tracks locks")
      msg.type = SEQUENCE_MSG_LOCK_TRACKS;
      sprintf (msg.text, "tracks=%p", tl);
      msg_send (sequence->msg, &msg, MSG_ACK);
      DEBUG("Tracks locks ack'ed. Freeing temporary data.")
      free (tl);
    }

  /* Duplicates all tracks data so that we don't interfer with the audio thread */
  sequence_track_t *new_tracks = calloc (tracks_num, sizeof
                                         (sequence_track_t));
  int tn = (sequence->tracks_num > tracks_num) ? tracks_num :
    sequence->tracks_num;
  memcpy (new_tracks, sequence->tracks, tn * sizeof (sequence_track_t));

  /* Unregister jack ports and wipes associated tracks data if tracks_num decreases. */
  sequence_track_t *t;
  for (i = tracks_num; i < sequence->tracks_num; i++)
    sequence_destroy_track (sequence, i, 0);

  /* Register jack ports and allocate new tracks data if tracks_num increases */
  for (i = sequence->tracks_num; i < tracks_num; i++)
    {
      DEBUG("Initializing track %d",i);
      t = new_tracks + i;
      sequence_track_init (t);
      t->channels = calloc (t->channels_num, sizeof (jack_port_t *));
      t->buffers = calloc (t->channels_num, sizeof (float *));
      t->frame_pos = malloc (sizeof (jack_nframes_t));
      *(t->frame_pos) = 0;

      j = i;
      do sprintf (t->name, "track%d", j++);
      while (sequence_track_name_exists (sequence, t->name));

      sequence_register_track (sequence, t);
      if (sequence->auto_connect) sequence_connect_playback (sequence, t);
    }

  /* Resize beats memory */
  for (i = 0; i < tracks_num; i++)
    {
      (new_tracks + i)->beats = calloc (beats_num, 1);
      (new_tracks + i)->mask = calloc (beats_num, 1);
      memset ((new_tracks + i)->mask, 1, beats_num);
      if (i < sequence->tracks_num)
       {
        if (sequence->beats_num > beats_num)
         {
          memcpy ((new_tracks + i)->beats, (sequence->tracks + i)->beats, beats_num);
          memcpy ((new_tracks + i)->mask, (sequence->tracks + i)->mask, beats_num);
         }
        else
         {
          if (duplicate_beats)
           {
            int bn;
            for (j=0; j < beats_num; j += sequence->beats_num)
             {
              bn = (beats_num - j) < sequence->beats_num 
                 ? beats_num - j
                 : sequence->beats_num;
              memcpy ((new_tracks + i)->beats + j, 
                      (sequence->tracks + i)->beats, bn);
                memcpy ((new_tracks + i)->mask + j, (sequence->tracks + i)->mask, bn);
             }
           }
          else
            memcpy ((new_tracks + i)->beats, (sequence->tracks + i)->beats, 
                    sequence->beats_num);
            memcpy ((new_tracks + i)->mask, (sequence->tracks + i)->mask, 
                    sequence->beats_num);
         }
       }
    }

  /* Packs all that and sends it to the audio thread */
  sprintf (msg.text, "tracks=%p tracks_num=%d beats_num=%d measure_len=%d",
            new_tracks, tracks_num, beats_num, measure_len);

  DEBUG("Sending resize message of length : %d",strlen(msg.text));
  sequence_track_t *old_tracks = sequence->tracks;
  int old_tracks_num = sequence->tracks_num;

  msg.type = SEQUENCE_MSG_RESIZE;
  msg_send (sequence->msg, &msg, MSG_ACK);

  /* Cleans up old data */
  DEBUG("Cleaning up old tracks data at %p", old_tracks);
  if (old_tracks)
    {
      for (i = 0; i < old_tracks_num; i++)
       {
        free ((old_tracks + i)->beats);
        if (old_tracks[i].mask) free ((old_tracks + i)->mask);
       }
      free (old_tracks);
    }
  
  DUMP(sequence->tracks->beats, sequence->beats_num);
}

void
sequence_remove_track (sequence_t *sequence, int track)
{
  sequence_msg_t msg;

  msg.type = SEQUENCE_MSG_LOCK_SINGLE_TRACK;
  sprintf (msg.text, "track=%d", track);
  msg_send (sequence->msg, &msg, MSG_ACK);

  sequence_destroy_track (sequence, track, 1);
  
  sequence_track_t *new_tracks = calloc (sequence->tracks_num - 1, sizeof (sequence_track_t));
  memcpy (new_tracks, sequence->tracks, track * sizeof (sequence_track_t));
  memcpy (new_tracks + track, sequence->tracks + track + 1, 
          (sequence->tracks_num - track - 1) * sizeof (sequence_track_t));

  sequence_track_t *old_tracks = sequence->tracks;

  msg.type = SEQUENCE_MSG_RESIZE;
  sprintf (msg.text, "tracks=%p tracks_num=%d beats_num=%d measure_len=%d",
            new_tracks, sequence->tracks_num - 1, sequence->beats_num, sequence->measure_len);
  msg_send (sequence->msg, &msg, MSG_ACK);

  free (old_tracks);
}

char *
sequence_get_name (sequence_t * sequence)
{
  return sequence->name;
}

void
sequence_set_transport (sequence_t * sequence, char respond, char query)
{
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_SET_TRANSPORT;
  sprintf (msg.text, "aware=%d query=%d", respond, query); 
  msg_send (sequence->msg, &msg, 0);
}

int
sequence_is_playing (sequence_t * sequence)
{
  jack_position_t t;

  return (sequence->status == SEQUENCE_ENABLED &&
          (!sequence->transport_aware ||
           jack_transport_query (sequence->client,
                                 &t) == JackTransportRolling));
}

int
sequence_get_active_beat (sequence_t * sequence, int track)
{
  return sequence->tracks[track].active_beat;
}

float
sequence_get_level (sequence_t * sequence, int track)
{
  return sequence->tracks[track].current_level /
   sequence->tracks[track].level_peak;
}

int
sequence_get_active_mask_beat (sequence_t * sequence, int track)
{
  return sequence->tracks[track].active_mask_beat;
}

void
sequence_unset_looping (sequence_t * sequence)
{
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_UNSET_LOOPING;
  msg.text[0] = '\0';
  msg_send (sequence->msg, &msg, 0);
}

int
sequence_is_looping (sequence_t * sequence)
{
  return sequence->looping;
}

void
sequence_set_looping (sequence_t * sequence)
{
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_SET_LOOPING;
  msg.text[0] = '\0';
  msg_send (sequence->msg, &msg, 0);
}


void
sequence_set_auto_connect (sequence_t * sequence, char status)
{
  sequence->auto_connect = status;
}

char
sequence_is_auto_connecting (sequence_t * sequence)
{
  return sequence->auto_connect;
}

float
sequence_get_bpm (sequence_t * sequence)
{
  return sequence->bpm;
}

void
sequence_set_bpm (sequence_t * sequence, float bpm)
{
  DEBUG("Setting bpm to : %f", bpm);
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_SET_BPM;
  sprintf (msg.text, "bpm=%f", bpm);
  msg_send (sequence->msg, &msg, 0);
}

void
sequence_set_beat (sequence_t * sequence, int track, int beat, char status)
{
  DEBUG ("sequence: %p, track: %d, beat:%d, status: %d", sequence, track,
         beat, status)
  sequence->tracks[track].beats[beat] = status;
}

int
sequence_get_tracks_num (sequence_t * sequence)
{
  return sequence->tracks_num;
}

int
sequence_get_beats_num (sequence_t * sequence)
{
  return sequence->beats_num;
}

int
sequence_get_measure_len (sequence_t * sequence)
{
  return sequence->measure_len;
}

char
sequence_get_beat (sequence_t * sequence, int track, int beat)
{
  return (sequence->tracks + track)->beats[beat];
}

char *
sequence_get_track_name (sequence_t * sequence, int track)
{
  return (sequence->tracks + track)->name;
}

int
sequence_track_name_exists (sequence_t *sequence, char *name)
{
  int i;
  for (i=0; i < sequence->tracks_num; i++)
   {
    if (!strcmp ((sequence->tracks + i)->name, name))
      return 1;
   }
  return 0;
}

sample_t *
sequence_get_sample (sequence_t * sequence, int track)
{
  return (sequence->tracks + track)->sample;
}

void
sequence_set_sample (sequence_t * sequence, int track, sample_t * sample)
{
  sequence_msg_t msg;
  sequence_track_t *t = sequence->tracks + track;
  int j, sr_converter_error;
  if (t->channels_num != sample->channels_num)
    {
      msg.type = SEQUENCE_MSG_LOCK_SINGLE_TRACK;
      sprintf (msg.text, "track=%d", track);
      msg_send (sequence->msg, &msg, MSG_ACK);
      if (t->channels_num) {
        for (j = 0; j < t->channels_num; j++)
          jack_port_unregister (sequence->client, t->channels[j]);
        free (t->channels);
        free (t->buffers);
      }
      t->channels_num = sample->channels_num;
      t->channels = calloc (t->channels_num, sizeof (jack_port_t *));
      t->buffers = calloc (t->channels_num, sizeof (float *));

      sequence_register_track (sequence, t);

      if (t->sr_converter != NULL) 
       {
        src_delete (t->sr_converter);
        t->sr_converter = NULL;
        free (t->sr_converter_buffer);
        t->sr_converter_buffer = NULL;
       }
    }
  
  if (t->sr_converter_type == -1) 
    t->sr_converter_type = sequence->sr_converter_default_type;
  
  if (t->sr_converter_type == SEQUENCE_SINC)
   {
    if (t->sr_converter == NULL) 
     {
      DEBUG("Creating SR converter for %d channels", t->channels_num);
      t->sr_converter = src_new (SRC_SINC_FASTEST, t->channels_num,
                                   &sr_converter_error);
      if (t->sr_converter == NULL)
       {
        DEBUG ("FATAL: Cannot create the sample rate converter : %s", 
               src_strerror (sr_converter_error));
        exit(1);
       }
     }
    else 
     {
      sr_converter_error = src_reset (t->sr_converter);
      if (sr_converter_error)
        DEBUG ("Cannot reset the sample rate converter : %s", 
               src_strerror (sr_converter_error));
     }
   }
  if (t->sr_converter_buffer == NULL)
    t->sr_converter_buffer = calloc (sequence->buffer_size * t->channels_num,
                                     sizeof (float));
  t->sr_converter_ratio = (double) sequence->framerate 
                          / (double) sample->framerate 
                          / pow (2, t->pitch / 12);
                      
  t->level_peak = sample->peak > 0 ? sample->peak : 1;

  msg.type = SEQUENCE_MSG_SET_SAMPLE;
  sprintf (msg.text, "track=%d sample=%p", track, sample);
  msg_send (sequence->msg, &msg, MSG_ACK);
  if (sequence->auto_connect) sequence_connect_playback (sequence, t);
}

void
sequence_set_track_name (sequence_t * sequence, int track, char *name)
{
  int j;
  sequence_track_t *t = sequence->tracks + track;
  strcpy (t->name, name);
  for (j = 0; j < t->channels_num; j++)
    {
      char *name = sequence_make_port_name (t, j);
      jack_port_set_name (t->channels[j], name);
      free(name);
    }
}

int
sequence_get_sample_usage (sequence_t * sequence, sample_t * sample)
{
  int i;
  int r = 0;
  for (i = 0; i < sequence->tracks_num; i++) 
   {
    if ((sequence->tracks + i)->sample) {
      DEBUG ("Track %d has sample : %s", i, (sequence->tracks +
                                           i)->sample->name);
    }
    if ((sequence->tracks + i)->sample == sample) r++;
   }
  DEBUG ("Usage for sample \"%s\" is : %d", sample->name, r);
  return r;
}

sequence_track_type_t
sequence_get_track_type (sequence_t * sequence, int track)
{
  sequence_track_type_t r;
  sequence_track_t *t = (sequence->tracks + track);
  if (t->sample)
    r = SAMPLE;
  else
    r = EMPTY;
  return r;
}


void
sequence_mute_track (sequence_t * sequence, char status, int track)
{
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_MUTE_TRACK;
  sprintf (msg.text, "track=%d mute=%d", track, (status) ? 0 : 1);
  msg_send (sequence->msg, &msg, MSG_ACK);
}

char
sequence_track_is_muted (sequence_t * sequence, int track)
{
  DEBUG("track %d is %s", track, (sequence->tracks[track].enabled) ? "not muted" : "muted");
  return (!sequence->tracks[track].enabled);
}

jack_nframes_t 
sequence_get_framerate (sequence_t *sequence)
{
  return sequence->framerate;
}

void
sequence_enable_mask (sequence_t *sequence, int track)
{
  if (!sequence->tracks[track].mask)
   {
    char *mask = calloc (sequence->beats_num, 1);
    memset (mask, 1, sequence->beats_num);
  
    sequence_msg_t msg;
    msg.type = SEQUENCE_MSG_ENABLE_MASK;
    sprintf (msg.text, "mask=%p track=%d", mask, track);
    msg_send (sequence->msg, &msg, MSG_ACK);
   }
}

void
sequence_disable_mask (sequence_t *sequence, int track)
{
  if (sequence->tracks[track].mask)
   {
    char *old_mask = sequence->tracks[track].mask;
  
    sequence_msg_t msg;
    msg.type = SEQUENCE_MSG_DISABLE_MASK;
    sprintf (msg.text, "track=%d", track);
    msg_send (sequence->msg, &msg, MSG_ACK);
    free (old_mask);
   }
}

void
sequence_set_mask_beat (sequence_t * sequence, int track, int beat, char status)
{
  DEBUG ("(mask)sequence: %p, track: %d, beat:%d, status: %d", sequence, track,
         beat, status)
  if (sequence->tracks[track].mask)
    sequence->tracks[track].mask[beat] = status;
}

char
sequence_get_mask_beat (sequence_t * sequence, int track, int beat)
{
  return sequence->tracks[track].mask[beat];
}

int
sequence_is_enabled_mask (sequence_t *sequence, int track)
{
  return (sequence->tracks[track].mask) ? 1 : 0;
}

void
sequence_set_pitch (sequence_t *sequence, int track, double pitch)
{
  DEBUG ("Setting pitch on track %d to : %f", track, pitch);
  sequence_msg_t msg;
  sequence->tracks[track].pitch = pitch;
  if (sequence->tracks[track].sample != NULL)
   {
    double ratio = (double) sequence->framerate 
                   / (double) sequence->tracks[track].sample->framerate 
                   / pow (2, pitch / 12);
    msg.type = SEQUENCE_MSG_SET_SR_RATIO;
    sprintf (msg.text, "track=%d ratio=%f", track, ratio);
    msg_send (sequence->msg, &msg, 0);
   }
}

double
sequence_get_pitch (sequence_t *sequence, int track)
{
  return sequence->tracks[track].pitch;
}

void
sequence_set_volume (sequence_t *sequence, int track, double volume)
{
  DEBUG ("Setting volume on track %d to : %f", track, volume);
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_SET_VOLUME;
  sprintf (msg.text, "track=%d volume=%f", track, volume);
  msg_send (sequence->msg, &msg, 0);
}

double
sequence_get_volume (sequence_t *sequence, int track)
{
  return sequence->tracks[track].volume;
}

void
sequence_set_smoothing (sequence_t *sequence, int track, int status)
{
  DEBUG ("Setting smoothing on track %d to : %d", track, status);
  sequence_msg_t msg;
  msg.type = SEQUENCE_MSG_SET_SMOOTHING;
  sprintf (msg.text, "track=%d status=%d", track, status);
  msg_send (sequence->msg, &msg, 0);
}

int
sequence_get_smoothing (sequence_t *sequence, int track)
{
  return sequence->tracks[track].smoothing;
}


void 
sequence_set_resampler_type (sequence_t * sequence, int type)
{
  if (type == -1) return;
  DEBUG ("Setting resampler type to : %d", type);
  sequence_msg_t msg;
  int restart = 0;
  if (sequence->status == SEQUENCE_ENABLED)
   {
    msg.type = SEQUENCE_MSG_ACK_DISABLE;
    msg_send (sequence->msg, &msg, MSG_ACK);
    restart = 1;
   }

  sequence->sr_converter_default_type = type;
  int i;
  for (i=0; i < sequence->tracks_num; i++)
   {
    sequence_track_t *t = sequence->tracks + i;
    t->sr_converter_type = type;
    if (t->sample != NULL)
     {
      int sr_converter_error;
      if (t->sr_converter != NULL) 
       {
        DEBUG ("Destroying SR converter on track %d", i);
        src_delete (t->sr_converter);
        t->sr_converter = NULL;
       }
      if (type == SEQUENCE_SINC)
       {
        DEBUG ("Creating SR converter of type %d with %d channel(s) on track %d",
               SRC_SINC_FASTEST, t->channels_num, i);
        t->sr_converter = src_new (SRC_SINC_FASTEST, t->channels_num, &sr_converter_error);
       }
     }
   }

  if (restart)
   {
    msg.type = SEQUENCE_MSG_ACK_ENABLE;
    msg_send (sequence->msg, &msg, MSG_ACK);
   }
  
}

int
sequence_get_resampler_type (sequence_t *sequence)
{
  return sequence->sr_converter_default_type;
}

void static 
sequence_export_mix (sequence_t *sequence, float *output, int nframes)
{
  int i, j, k;

  for (k = 0; k < nframes * 2; k++) 
    output[k] = 0;

  for (i = 0; i < sequence->tracks_num; i++) 
   {
    sequence_track_t *track = sequence->tracks + i;

    for (j = 0; j < track->channels_num; j++)
      for (k = 0; k < nframes; k++)
        output[k * 2 + (j % 2)] += track->buffers[j][k];

    for ( ; j < 2; j++)
      for (k = 0; k < nframes; k++)
        output[k * 2 + j] += track->buffers[j % track->channels_num][k];
   }
}

void
sequence_export (sequence_t *sequence, char *filename,  
                 progress_callback_t progress_callback, void *progress_data)
{
  jack_nframes_t bufsize;
  SF_INFO info;
  SNDFILE *fd;
  int i, j;
  float *output;
  jack_nframes_t nframes, pos, nframes_played, sequence_nframes;
  sequence_msg_t msg;
  sequence_t *sequence_tmp;
  sequence_track_t *track;

  DEBUG("Exporting sequence to file: %s", filename);

  progress_callback ("Preparing to export...", 0, progress_data);

  bufsize = sequence->buffer_size;

  // Stopping sequence
  if (sequence->status == SEQUENCE_ENABLED)
   {
    msg.type = SEQUENCE_MSG_ACK_DISABLE;
    msg_send (sequence->msg, &msg, MSG_ACK);
   }

  sequence_nframes = sequence->beats_num * sequence_get_beat_nframes (sequence);

  sequence_tmp = malloc (sizeof (sequence_t));
  memcpy (sequence_tmp, sequence, sizeof (sequence_t));

  sequence_tmp->tracks = calloc (sequence->tracks_num, sizeof (sequence_track_t));
  memcpy (sequence_tmp->tracks, sequence->tracks, sequence->tracks_num * sizeof (sequence_track_t));

  sequence_tmp->looping = 0;

  // Allocating temporary buffers
  for (i = 0; i < sequence->tracks_num; i++) 
   {
    track = sequence_tmp->tracks + i;
    track->buffers = calloc (track->channels_num, sizeof (float *));
    for (j = 0; j < track->channels_num; j++)
      track->buffers[j] = calloc (bufsize, sizeof(float));
    *(track->frame_pos) = 0;
    track->active_beat = -1;
   }

  output = calloc (2 * bufsize, sizeof(float));

  // Opening file for writing
  info.samplerate = sequence->framerate;
  info.channels = 2;
  info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
  fd = sf_open (filename, SFM_WRITE, &info); // Needs error checking
  if (fd) { DEBUG("Successfully opened outfile"); }
  else { DEBUG("FAILED to open outfile"); sf_perror(fd); }

  // Finding level peak
  float peak = 0;
  for (pos = 0; pos < sequence_nframes; pos += bufsize) 
   {
    progress_callback ("Computing level peak", 0.1 + (float) pos / sequence_nframes * 0.3, progress_data);
    nframes = pos + bufsize < sequence_nframes ? bufsize : sequence_nframes - pos;
    sequence_do_process (sequence_tmp, pos, nframes);
    sequence_export_mix (sequence_tmp, output, nframes);
    for (i = 0; i < (nframes * 2); i++) if (fabsf (output[i]) > peak) peak = fabsf(output[i]);
   }

  for (; (nframes_played = sequence_do_process (sequence_tmp, pos, bufsize)); pos += bufsize) 
   {
    sequence_export_mix (sequence_tmp, output, nframes_played);
    for (i = 0; i < (nframes_played * 2); i++) if (fabsf (output[i]) > peak) peak = fabsf(output[i]);
   }

  DEBUG ("level peak is: %f", peak);

  // Writing to file
  for (pos = 0; pos < sequence_nframes; pos += bufsize) 
   {
    progress_callback ("Mixing and writing to file", 0.4 + (float) pos / sequence_nframes * 0.6, progress_data);
    nframes = pos + bufsize < sequence_nframes ? bufsize : sequence_nframes - pos;
    sequence_do_process (sequence_tmp, pos, nframes);
    sequence_export_mix (sequence_tmp, output, nframes);
    for (i = 0; i < (nframes * 2); i++) output[i] /= peak;
    sf_writef_float (fd, output, nframes);
   }

  for (; (nframes_played = sequence_do_process (sequence_tmp, pos, bufsize)); pos += bufsize) 
   {
    sequence_export_mix (sequence_tmp, output, nframes_played);
    for (i = 0; i < (nframes_played * 2); i++) output[i] /= peak;
    sf_writef_float (fd, output, nframes_played);
   }

  sf_close (fd);

  // Freeing temporary buffers
  free(output);
  for (i = 0; i < sequence->tracks_num; i++) 
   {
    track = sequence_tmp->tracks + i;
    for (j = 0; j < track->channels_num; j++) {
      free (track->buffers[j]);
    }
    free (track->buffers);
   }
  free (sequence_tmp->tracks);
  free (sequence_tmp);

  progress_callback ("Done", 1, progress_data);

}



Generated by  Doxygen 1.6.0   Back to index