#include "config.h"

#ifdef USE_AUDIO
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>

#ifdef USE_SDL1
#include <SDL/SDL.h>
#endif
#ifdef USE_SDL2
#include <SDL2/SDL.h>
#endif

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "const.h"
#include "nlllib.h"
#include "memory.h"
#include "audio.h"
#include "sound.h"
#include "sdl.h"

struct aunit {
  struct aunit *next;
  int index;
  unsigned int flags;

  struct {
    int amp;
    int freq;
    int phase;
    int type;
    int mod;
    unsigned int flags;
  } osc;

  int outnum;
  struct {
    int amp;
    struct aunit *aunit;
  } outs[AUDIO_UNIT_OUTMAX];

  struct {
    int num;
    int amp;
    struct aunit *aunit;
  } in;

  struct {
    int amp;
  } out;

  struct {
    int num;
    struct envpoint {
      int step;
      int amp;
      unsigned int flags;
    } points[AUDIO_UNIT_ENVMAX];
  } envelope;

  struct {
    struct {
      int num;
      struct {
	int amp;
	unsigned int flags;
      } points[AUDIO_UNIT_FILFWMAX];
    } forward;
    struct {
      int num;
      struct {
	int amp;
	unsigned int flags;
      } points[AUDIO_UNIT_FILBWMAX];
    } backward;
  } filter;
};

static struct aunitset {
  int num;
  struct aunit *aunits;
} aunitsets[AUDIO_UNITSET_MAXNUM];

struct aunit_regs {
  struct aunit_regs *next;

  struct aunit *aunit;

  struct {
    int amp;
  } out;

  struct {
    int index;
  } envelope;

  struct {
    int index;
    struct {
      int amp;
    } out[AUDIO_UNIT_BUFNUM];
  } buffer;
};

struct samplestep {
  int sec;
  int count;
};

struct player {
  struct player *next;
  struct player *nextplayer;
  struct aunit_regs *pool;
  int duplicate;

  struct player *head;
  int aunitset_index;
  struct aunit_regs *regs;
  int amp;
  int freq;
  struct samplestep step;
  int stepbase;
  int steplen;
};

static int sampling_freq = AUDIO_DEFAULT_SAMPLING_FREQ;

static struct player *playeres = NULL;

static struct aunit *aunit_pool = NULL;
static struct aunit_regs *aunit_regs_pool = NULL;
static struct player *player_pool = NULL;

static struct aunit *_aunit_alloc(void)
{
  struct aunit *aunit;
  if (aunit_pool) {
    aunit = aunit_pool;
    aunit_pool = aunit_pool->next;
  } else {
    aunit = memory_alloc(sizeof(*aunit));
  }
  return aunit;
}

static void _aunit_free(struct aunit *aunit)
{
  aunit->next = aunit_pool;
  aunit_pool = aunit;
}

static struct aunit_regs *_aunit_regs_alloc(void)
{
  struct aunit_regs *aunit_regs;
  if (aunit_regs_pool) {
    aunit_regs = aunit_regs_pool;
    aunit_regs_pool = aunit_regs_pool->next;
  } else {
    aunit_regs = memory_alloc(sizeof(*aunit_regs));
  }
  return aunit_regs;
}

static void _aunit_regs_free(struct aunit_regs *aunit_regs)
{
  aunit_regs->next = aunit_regs_pool;
  aunit_regs_pool = aunit_regs;
}

static struct player *_player_alloc(void)
{
  struct player *player;
  if (player_pool) {
    player = player_pool;
    player_pool = player_pool->next;
  } else {
    player = memory_alloc(sizeof(*player));
  }
  return player;
}

static void _player_free(struct player *player)
{
  player->next = player_pool;
  player_pool = player;
}

static int aunit_regs_free(struct aunit_regs *aunit_regs)
{
  struct aunit_regs *next;

  for (; aunit_regs; aunit_regs = next) {
    next = aunit_regs->next;
    _aunit_regs_free(aunit_regs);
  }

  return 0;
}

static struct aunit_regs *aunit_regs_alloc_num(int num)
{
  struct aunit_regs *aunit_regs = NULL, *regs;
  int i;

  for (i = 0; i < num; i++) {
    if ((regs = _aunit_regs_alloc()) == NULL)
      goto err;
    memset(regs, 0, sizeof(*regs));

    regs->next = aunit_regs;
    aunit_regs = regs;
  }

  return aunit_regs;

err:
  aunit_regs_free(aunit_regs);
  return NULL;
}

static int aunit_regs_push(struct aunit_regs **pool, struct aunit_regs *aunit_regs)
{
  struct aunit_regs *regs;

  while (aunit_regs) {
    regs = aunit_regs;
    aunit_regs = aunit_regs->next;

    regs->next = *pool;
    *pool = regs;
  }

  return 0;
}

static struct aunit_regs *aunit_regs_pop(struct aunit_regs **pool, struct aunitset *aunitset)
{
  struct aunit_regs *aunit_regs = NULL, *regs;
  struct aunit *aunit;

  for (aunit = aunitset->aunits; aunit; aunit = aunit->next) {
    if ((regs = *pool) == NULL)
      goto err;
    *pool = (*pool)->next;
    memset(regs, 0, sizeof(*regs));

    regs->aunit = aunit;

    regs->next = aunit_regs;
    aunit_regs = regs;
  }

  return aunit_regs;

err:
  aunit_regs_push(pool, aunit_regs);
  return NULL;
}

static int step_clear(struct samplestep *step)
{
  step->sec   = 0;
  step->count = 0;
  return 0;
}

static int step_num(struct samplestep *step, int samplefreq)
{
  return step->sec * samplefreq + step->count;
}

static int step_diff(struct samplestep *step0, struct samplestep *step1, int samplefreq)
{
  return (step0->sec - step1->sec) * samplefreq + step0->count - step1->count;
}

static int step_copy(struct samplestep *dst, struct samplestep *src)
{
  dst->sec   = src->sec;
  dst->count = src->count;
  return 0;
}

static int step_add(struct samplestep *dst, struct samplestep *src, int count, int samplefreq)
{
  struct samplestep step;

  if (!src)
    src = dst;

  step_copy(&step, src);

  step.count += count;
  while (step.count >= samplefreq) {
    step.sec++;
    step.count -= samplefreq;
  }

  step_copy(dst, &step);

  return 0;
}

static struct player *player_alloc(int aunitset_index, int amp, int freq,
				   struct samplestep *step, int stepbase, int steplen)
{
  struct player *player;

  if ((player = _player_alloc()) == NULL)
    return NULL;
  memset(player, 0, sizeof(*player));

  player->amp      = amp;
  player->freq     = freq;
  step_copy(&player->step, step);
  player->stepbase = stepbase;
  player->steplen  = steplen;

  player->aunitset_index = aunitset_index;

  return player;
}

static int player_free(struct player *player)
{
  struct player *next;

  for (; player; player = next) {
    if (player->nextplayer) {
      player_free(player->nextplayer);
      player->nextplayer = NULL;
    }
    next = player->next;
    aunit_regs_free(player->regs);
    aunit_regs_free(player->pool);
    _player_free(player);
  }

  return 0;
}

static struct player *_player_copy(struct player *player)
{
  return player_alloc(player->aunitset_index, player->amp, player->freq,
		      &player->step, player->stepbase, player->steplen);
}

static struct player *player_copy(struct player *player)
{
  struct player *dst = NULL, *p, **pp, *head;

  pp = &dst;
  head = player;

  for (; player; player = player->next) {
    p = _player_copy(player);
    if (!p)
      goto err;
    p->head = head;
    if (player->nextplayer) {
      p->nextplayer = player_copy(player->nextplayer);
      if (!p->nextplayer)
	goto err;
    }
    *pp = p;
    pp = &p->next;
  }

  return dst;

err:
  player_free(dst);
  return NULL;
}

#define SCALENUM 12

static int scale2freq[SCALENUM] = {
  16744036, /* C9  */
  17739688, /* C#9 */
  18794545, /* D9  */
  19912127, /* D#9 */
  21096164, /* E9  */
  22350607, /* F9  */
  23679643, /* F#9 */
  25087708, /* G9  */
  26579501, /* G#9 */
  28160000, /* A9  */
  29834481, /* A#9 */
  31608531, /* B9  */
};

static int player_add(struct player **pp, int aunitset_index,
		      int amp, int scale, int octave,
		      struct samplestep *step, int stepbase, int steplen)
{
  struct player *player;
  int freq = 0, i;

  if (scale >= 0) {
    freq = scale2freq[scale];
    for (i = octave; i < 9; i++)
      freq >>= 1;
    freq /= 1000;
  }

  player = player_alloc(aunitset_index, amp, freq, step, stepbase, steplen);
  if (!player)
    return -1;

  player->next = *pp;
  *pp = player;

  return 0;
}

struct audio_data {
  struct audio_data *next;
  volatile int finished;
  int num;
  int samplefreq;
  int channels;
  int samples;
  struct samplestep length;
  struct samplestep step;
  struct player *ready;
  struct player *busy;
  struct player *complete;
#ifdef USE_SDL
  SDL_Thread *thread;
#endif
};

static struct audio_data *audiodata_list = NULL;

static struct audio_data *audiodata_alloc(void)
{
  struct audio_data *audiodata;
  audiodata = memory_alloc(sizeof(*audiodata));
  if (audiodata)
    memset(audiodata, 0, sizeof(*audiodata));
  return audiodata;
}

static int _wait(struct audio_data *audiodata)
{
#ifdef USE_SDL
  int status;
#endif

#ifdef USE_SDL
  SDL_WaitThread(audiodata->thread, &status);
#endif

#ifdef AUDIO_EXTERNAL_DRIVER
  ext_audio_wait();
#endif

  return 0;
}

static int audiodata_free(struct audio_data *audiodata)
{
  struct player *player;

  _wait(audiodata);

  while (audiodata->ready) {
    player = audiodata->ready;
    audiodata->ready = audiodata->ready->nextplayer;

    player->nextplayer = audiodata->complete;
    audiodata->complete = player;
  }

  while (audiodata->busy) {
    player = audiodata->busy;
    audiodata->busy = audiodata->busy->nextplayer;

    player->nextplayer = audiodata->complete;
    audiodata->complete = player;
  }

  player_free(audiodata->complete);

  memory_free(audiodata);

  return 0;
}

static int audiodata_list_clean(void)
{
  struct audio_data *audiodata, **pp;
  int n = 0;

  for (pp = &audiodata_list; *pp;) {
    audiodata = *pp;
    if (audiodata->finished) {
      *pp = audiodata->next;
      audiodata->next = NULL;
      audiodata_free(audiodata);
      continue;
    }
    n++;
    pp = &(*pp)->next;
  }

  return n;
}

static int audiodata_list_wait(void)
{
  while (audiodata_list_clean() > 0)
    nll_sleep(1);
  return 0;
}

static int audiodata_list_stop(void)
{
  struct audio_data *audiodata;
  for (audiodata = audiodata_list; audiodata; audiodata = audiodata->next)
    audiodata->finished = 1;
  return audiodata_list_wait();
}

static int audiodata_list_num(void)
{
  struct audio_data *audiodata;
  int num = 0;

  for (audiodata = audiodata_list; audiodata; audiodata = audiodata->next)
    num++;

  return num;
}

static int init(void)
{
  static int initialized = 0;

  if (!initialized) {
    memset(aunitsets, 0, sizeof(aunitsets));
#ifdef USE_SDL
    sdl_start();
#endif
    initialized = 1;
  }

  return 0;
}

int audio_check(void)
{
  int i;

  if (audiodata_list)
    return NLL_ERRCODE_MEMORY_NOT_EMPTY;

  if (playeres)
    return NLL_ERRCODE_MEMORY_NOT_EMPTY;

  for (i = 0; i < AUDIO_UNITSET_MAXNUM; i++) {
    if (aunitsets[i].aunits || aunitsets[i].num)
      return NLL_ERRCODE_MEMORY_NOT_EMPTY;
  }

  return 0;
}

int audio_init(void)
{
  int r = 0;
#ifdef AUDIO_EXTERNAL_DRIVER
  r = ext_audio_init();
#endif
  return r;
}

int audio_done(void)
{
  int r;
  struct aunit *aunit;
  struct aunit_regs *aunit_regs;
  struct player *player;

  audiodata_list_stop();

  if ((r = player_free(playeres)) < 0)
    return r;
  playeres = NULL;

  if ((r = audio_clear_all()) < 0)
    return r;

  while (aunit_pool) {
    aunit = aunit_pool;
    aunit_pool = aunit_pool->next;
    memory_free(aunit);
  }

  while (aunit_regs_pool) {
    aunit_regs = aunit_regs_pool;
    aunit_regs_pool = aunit_regs_pool->next;
    memory_free(aunit_regs);
  }

  while (player_pool) {
    player = player_pool;
    player_pool = player_pool->next;
    memory_free(player);
  }

#ifdef AUDIO_EXTERNAL_DRIVER
  ext_audio_done();
#endif

  return 0;
}

static int muldiv(int val, int mul, int div)
{
  /*
   * To avoid overflow, calculate division and modulo separately
   * (val * mul / div)
   */
  return ((val / div) * mul) + ((val % div) * mul / div);
}

#ifdef AUDIO_SIN_DATA_RAW
#define AMPLIMIT 32767
#define SINNUM 64
static const int sin_val[SINNUM + 1] = {
      0,   804,  1608,  2410,  3212,  4011,  4808,  5602,
   6393,  7179,  7962,  8739,  9512, 10278, 11039, 11793,
  12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530,
  18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594,
  23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790,
  27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956,
  30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971,
  32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757, 32767
};
#else
#define AMPLIMIT 32767
#define SINNUM 256
static const int sin_val[SINNUM + 1] = {
      0,   201,   402,   603,   804,  1005,  1206,  1407,
   1608,  1809,  2009,  2210,  2410,  2611,  2811,  3012,
   3212,  3412,  3612,  3811,  4011,  4210,  4410,  4609,
   4808,  5007,  5205,  5404,  5602,  5800,  5998,  6195,
   6393,  6590,  6786,  6983,  7179,  7375,  7571,  7767,
   7962,  8157,  8351,  8545,  8739,  8933,  9126,  9319,
   9512,  9704,  9896, 10087, 10278, 10469, 10659, 10849,
  11039, 11228, 11417, 11605, 11793, 11980, 12167, 12353,
  12539, 12725, 12910, 13094, 13279, 13462, 13645, 13828,
  14010, 14191, 14372, 14553, 14732, 14912, 15090, 15269,
  15446, 15623, 15800, 15976, 16151, 16325, 16499, 16673,
  16846, 17018, 17189, 17360, 17530, 17700, 17869, 18037,
  18204, 18371, 18537, 18703, 18868, 19032, 19195, 19357,
  19519, 19680, 19841, 20000, 20159, 20317, 20475, 20631,
  20787, 20942, 21096, 21250, 21403, 21554, 21705, 21856,
  22005, 22154, 22301, 22448, 22594, 22739, 22884, 23027,
  23170, 23311, 23452, 23592, 23731, 23870, 24007, 24143,
  24279, 24413, 24547, 24680, 24811, 24942, 25072, 25201,
  25329, 25456, 25582, 25708, 25832, 25955, 26077, 26198,
  26319, 26438, 26556, 26674, 26790, 26905, 27019, 27133,
  27245, 27356, 27466, 27575, 27683, 27790, 27896, 28001,
  28105, 28208, 28310, 28411, 28510, 28609, 28706, 28803,
  28898, 28992, 29085, 29177, 29268, 29358, 29447, 29534,
  29621, 29706, 29791, 29874, 29956, 30037, 30117, 30195,
  30273, 30349, 30424, 30498, 30571, 30643, 30714, 30783,
  30852, 30919, 30985, 31050, 31113, 31176, 31237, 31297,
  31356, 31414, 31470, 31526, 31580, 31633, 31685, 31736,
  31785, 31833, 31880, 31926, 31971, 32014, 32057, 32098,
  32137, 32176, 32213, 32250, 32285, 32318, 32351, 32382,
  32412, 32441, 32469, 32495, 32521, 32545, 32567, 32589,
  32609, 32628, 32646, 32663, 32678, 32692, 32705, 32717,
  32728, 32737, 32745, 32752, 32757, 32761, 32765, 32766, 32767
};
#endif

static int degree_inrange(int deg)
{
  while (deg < 0)
    deg += (SINNUM*4);
  deg %= (SINNUM*4);
  return deg;
}

static int int_sin(int deg, int n)
{
  deg = degree_inrange(deg);
  if (deg >= (SINNUM*2))
    return -int_sin(deg - (SINNUM*2), n);
  if (deg > SINNUM)
    return int_sin((SINNUM*2) - deg, n);
  return sin_val[deg];
}

static int int_triangle(int deg, int n)
{
  deg = degree_inrange(deg);
  if (deg >= (SINNUM*2))
    return -int_triangle(deg - (SINNUM*2), n);

  n = n ? n : AUDIO_PER_NUM/2;
  n = muldiv(n, SINNUM * 2, AUDIO_PER_NUM);

  if (deg <= n) deg = muldiv(deg, SINNUM, n);
  else deg = muldiv(SINNUM*2 - deg, SINNUM, SINNUM*2 - n);

  return muldiv(deg, AMPLIMIT, SINNUM);
}

static int int_pulse(int deg, int n)
{
  deg = degree_inrange(deg);
  return (deg < muldiv(SINNUM*4, n, AUDIO_PER_NUM)) ? AMPLIMIT : -AMPLIMIT;
}

static int int_sawup(int deg, int n)
{
  deg = degree_inrange(deg);
  if (deg > (SINNUM*2))
    return -int_sawup((SINNUM*4) - deg, n);
  return int_triangle(deg, AUDIO_PER_NUM - n);
}

static int int_sawdown(int deg, int n)
{
  deg = degree_inrange(deg);
  return -int_sawup(deg, n);
}

static unsigned int _rand0(unsigned int seed)
{
  seed ^= seed << 13;
  seed ^= seed >> 17;
  seed ^= seed <<  5;
  return seed;
}

static unsigned int _rand1(unsigned int seed)
{
  seed ^= seed <<  1;
  seed ^= seed << 10;
  seed ^= seed >>  3;
  return seed;
}

static unsigned int _rand(unsigned int seed)
{
  return (seed >> 3) ^ _rand0((seed << 1) ^ _rand1(seed));
}

static int _random(int deg)
{
  return (int)(_rand(deg + 1) % (AMPLIMIT * 2 + 1)) - AMPLIMIT;
}

static int int_random(int deg, int n)
{
  int i, r = 0;
  n++;
  for (i = 0; i < n; i++)
    r += _random(deg + i * 7);
  if (n)
    r /= n;
  return r;
}

static int int_noise(int deg, int n)
{
  n = n ? n : AUDIO_PER_NUM;
  n = deg / muldiv(SINNUM*4, n, AUDIO_PER_NUM);
  return (int)(_rand(n + 1) % (AMPLIMIT * 2 + 1)) - AMPLIMIT;
}

static int int_zero(int deg, int n)
{
  return 0;
}

static int int_high(int deg, int amp)
{
  amp = amp ? amp : AUDIO_PER_NUM;
  amp = muldiv(amp, AMPLIMIT, AUDIO_PER_NUM);
  return amp;
}

static int int_inpulse(int deg, int amp)
{
  amp = amp ? amp : AUDIO_PER_NUM;
  amp = muldiv(amp, AMPLIMIT, AUDIO_PER_NUM);
  return (deg == 0) ? amp : 0;
}

static int int_data(int deg, int index)
{
  sound_t sound;
  short frames[2];
  int i, amp;

  sound = sound_get_sound(index);
  if (!sound)
    return 0;

  deg = muldiv(deg, sampling_freq, (SINNUM*4));
  deg = muldiv(deg, ((2*2*2*2*2*2) * 1000), scale2freq[9]); /* Default:O3A */
  sound->offset = muldiv(deg, sound->samplefreq, sampling_freq);

  if (sound_read(sound, frames, 1) < 0)
    return 0;

  amp = 0;
  for (i = 0; i < sound->channels; i++)
    amp += frames[i];
  amp /= sound->channels;

  return amp;
}

static int int_amp(int type, int deg)
{
  int a, param;

  param = type & ~NLL_A_TYPE_MASK;

  switch (type & NLL_A_TYPE_MASK) {
  case NLL_A_SIN:       a = int_sin(deg, param); break;
  case NLL_A_TRIANGLE:  a = int_triangle(deg, param); break;
  case NLL_A_SQUARE:    a = int_pulse(deg, AUDIO_PER_NUM / 2); break;
  case NLL_A_PULSE:     a = int_pulse(deg, param); break;
  case NLL_A_SAWUP:     a = int_sawup(deg, param); break;
  case NLL_A_SAWDOWN:   a = int_sawdown(deg, param); break;
  case NLL_A_RANDOM:    a = int_random(deg, param); break;
  case NLL_A_NOISE:     a = int_noise(deg, param); break;
  case NLL_A_ZERO:      a = int_zero(deg, param); break;
  case NLL_A_HIGH:      a = int_high(deg, param); break;
  case NLL_A_INPULSE:   a = int_inpulse(deg, param); break;
  case NLL_A_DATA:      a = int_data(deg, param); break;
  default:              a = 0; break;
  }

  return a;
}

static struct aunit *aunitset_search(struct aunitset *aunitset, int index)
{
  struct aunit *aunit;

  for (aunit = aunitset->aunits; aunit; aunit = aunit->next) {
    if (aunit->index == index)
      break;
  }

  return aunit;
}

static int aunit_regs_calc_envelope(struct aunit_regs *regs, int step)
{
  struct aunit *aunit;
  struct envpoint *p0;
  struct envpoint *p1;

  aunit = regs->aunit;

  if (!aunit->envelope.num)
    return AUDIO_PER_NUM;

  p0 = &aunit->envelope.points[regs->envelope.index];

  if (regs->envelope.index >= aunit->envelope.num - 1)
    return p0->amp;

  p1 = p0 + 1;

  if (p0->step >= p1->step)
    return p0->amp;

  return p0->amp + muldiv(p1->amp - p0->amp, step - p0->step, p1->step - p0->step);
}

static int aunit_regs_calc_filter_bw(struct aunit_regs *regs, int amp, int step)
{
  struct aunit *aunit;
  int i, n, a, base;

  aunit = regs->aunit;

  if (aunit->filter.backward.num > 0) {
    n = regs->buffer.index;
    for (i = 0; i < aunit->filter.backward.num; i++) {
      base = 0;

      a = aunit->filter.backward.points[i].amp;

      if (aunit->filter.backward.points[i].flags & NLL_A_FILINC)
	a = (step < AUDIO_PER_NUM) ? (base + muldiv(a - base, step, AUDIO_PER_NUM)) : a;
      else if (aunit->filter.backward.points[i].flags & NLL_A_FILDEC)
	a = (step < AUDIO_PER_NUM) ? (a - muldiv(a - base, step, AUDIO_PER_NUM)) : base;

      amp += muldiv(regs->buffer.out[n].amp, a, AUDIO_PER_NUM);
      n = (n > 0) ? (n - 1) : (AUDIO_UNIT_BUFNUM - 1);
      if (n == regs->buffer.index)
	break;
    }
  }

  return amp;
}

static int aunit_regs_calc_filter_fw(struct aunit_regs *regs, int amp, int step)
{
  struct aunit *aunit;
  int i, n, a, base;

  aunit = regs->aunit;

  if (aunit->filter.forward.num > 0) {
    amp = 0;
    n = regs->buffer.index;
    for (i = 0; i < aunit->filter.forward.num; i++) {
      base = (i == 0) ? AUDIO_PER_NUM : 0;

      a = aunit->filter.forward.points[i].amp;

      if (aunit->filter.forward.points[i].flags & NLL_A_FILINC)
	a = (step < AUDIO_PER_NUM) ? (base + muldiv(a - base, step, AUDIO_PER_NUM)) : a;
      else if (aunit->filter.forward.points[i].flags & NLL_A_FILDEC)
	a = (step < AUDIO_PER_NUM) ? (a - muldiv(a - base, step, AUDIO_PER_NUM)) : base;

      amp += muldiv(regs->buffer.out[n].amp, a, AUDIO_PER_NUM);
      n = (n > 0) ? (n - 1) : (AUDIO_UNIT_BUFNUM - 1);
      if (n == regs->buffer.index)
	break;
    }
  }

  return amp;
}

static int calc_deg(int step, int freq, int samplefreq)
{
  return muldiv(step * freq, SINNUM*4, samplefreq);
}

static int select_val(int val, int limit, int pval, int mval, int zval)
{
  if (val >  limit) return pval;
  if (val < -limit) return mval;
  return zval;
}

static int aunit_regs_calc(struct aunit_regs *aunit_regs, int amp, int freq,
			   int samplefreq, int step, int stepbase, int steplen)
{
  struct aunit_regs *regs;
  struct aunit *aunit;
  int i, deg, f, a, outamp = 0;
#ifdef AUDIO_AVERAGING_AMP_LEVEL
  int count = 0;
#endif
  int endstep, endstep_fixed, endstep_linear;

  if (!freq)
    return 0;

  if (!aunit_regs) {
    deg = calc_deg(step, freq, samplefreq);
#ifdef AUDIO_AVERAGING_AMP_LEVEL
    count = 1;
#endif
    outamp = int_amp(NLL_A_SIN, deg);
  }

  for (regs = aunit_regs; regs; regs = regs->next) {
    aunit = regs->aunit;
    aunit->in.num = 0;
    aunit->in.amp = 0;
    aunit->out.amp = 0;
  }

  for (regs = aunit_regs; regs; regs = regs->next) {
    aunit = regs->aunit;
    aunit->out.amp = regs->out.amp;
    for (i = 0; i < aunit->outnum; i++) {
      if (aunit->outs[i].aunit) {
	aunit->outs[i].aunit->in.num++;
	aunit->outs[i].aunit->in.amp += muldiv(regs->out.amp, aunit->outs[i].amp, AUDIO_PER_NUM);
      } else {
#ifdef AUDIO_AVERAGING_AMP_LEVEL
	count++;
#endif
	outamp += muldiv(regs->out.amp, aunit->outs[i].amp, AUDIO_PER_NUM);
      }
    }
  }

#ifdef AUDIO_AVERAGING_AMP_LEVEL
  for (regs = aunit_regs; regs; regs = regs->next) {
    aunit = regs->aunit;
    if (aunit->in.num)
      aunit->in.amp /= aunit->in.num;
  }

  if (count)
    outamp /= count;
#endif

  endstep_linear = (steplen  > 0) ? muldiv(step, AUDIO_PER_NUM, steplen ) : 0;
  endstep_fixed  = (stepbase > 0) ? muldiv(step, AUDIO_PER_NUM, stepbase) : 0;

  for (regs = aunit_regs; regs; regs = regs->next) {
    aunit = regs->aunit;

    a = 0;

    if ((aunit->osc.type & NLL_A_TYPE_MASK) == NLL_A_EXTIN) {
      if (aunit->in.aunit)
	a = aunit->in.aunit->out.amp;
    } else {
      f = aunit->osc.freq;

      if ((aunit->osc.mod & NLL_A_MOD_MASK) == NLL_A_FREQ) {
	f = f + muldiv(aunit->in.amp, f, AMPLIMIT);
      }

      if (aunit->osc.flags & NLL_A_FIXED) {
	deg = calc_deg(step, f, samplefreq);
      } else {
	deg = calc_deg(step, freq, samplefreq);
	deg = muldiv(deg, f, AUDIO_PER_NUM);
      }

      deg += muldiv(aunit->osc.phase, SINNUM*4, AUDIO_PER_NUM);

      if ((aunit->osc.mod & NLL_A_MOD_MASK) == NLL_A_PHASE) {
	deg = deg + muldiv(aunit->in.amp, SINNUM*2, AMPLIMIT);
      }

      a = int_amp(aunit->osc.type, deg);
    }

    a = muldiv(a, aunit->osc.amp, AUDIO_PER_NUM);

    switch (aunit->osc.mod & NLL_A_MOD_MASK) {
    case NLL_A_NULL: a =  aunit->in.amp; break;
    case NLL_A_REV:  a = a - aunit->in.amp; break;
    case NLL_A_ABS:  a = select_val(aunit->in.amp, a, aunit->in.amp, a + (a - aunit->in.amp), a + (a - aunit->in.amp)); break;
    case NLL_A_INV:  a = select_val(aunit->in.amp, a, a + (a - aunit->in.amp), -a + (-a - aunit->in.amp), aunit->in.amp); break;
    case NLL_A_SIGN: a = select_val(aunit->in.amp, 0, a, -a, 0); break;
    case NLL_A_CLIP: a = select_val(aunit->in.amp, a, a, -a, aunit->in.amp); break;
    case NLL_A_ADD:  a =  aunit->in.amp + a; break;
    case NLL_A_SUB:  a =  aunit->in.amp - a; break;
    case NLL_A_MUL:  a =  aunit->in.amp * a; break;
    case NLL_A_AND:  a =  aunit->in.amp & a; break;
    case NLL_A_OR:   a =  aunit->in.amp | a; break;
    case NLL_A_XOR:  a =  aunit->in.amp ^ a; break;
    case NLL_A_AVE:  a = (aunit->in.amp + a) / 2; break;
    case NLL_A_PER:  a = muldiv(aunit->in.amp, a, AMPLIMIT); break;
    case NLL_A_MIN:  a = (aunit->in.amp < a) ? aunit->in.amp : a; break;
    case NLL_A_MAX:  a = (aunit->in.amp > a) ? aunit->in.amp : a; break;
    case NLL_A_LT:   a = (aunit->in.amp < a) ? AMPLIMIT : -AMPLIMIT; break;
    case NLL_A_GT:   a = (aunit->in.amp > a) ? AMPLIMIT : -AMPLIMIT; break;
    case NLL_A_PHASE:
    case NLL_A_FREQ:
    default:
      break;
    }

    endstep = (aunit->flags & NLL_A_ENVFIXED) ? endstep_fixed : endstep_linear;

    while ((regs->envelope.index + 1 < aunit->envelope.num) &&
	   (aunit->envelope.points[regs->envelope.index + 1].step <= endstep)) {
      regs->envelope.index++;
    }
    a = muldiv(a, aunit_regs_calc_envelope(regs, endstep), AUDIO_PER_NUM);

    endstep = (aunit->flags & NLL_A_FILFIXED) ? endstep_fixed : endstep_linear;

    regs->buffer.out[regs->buffer.index].amp = a;
    a = aunit_regs_calc_filter_bw(regs, a, endstep);

    regs->buffer.out[regs->buffer.index].amp = a;
    a = aunit_regs_calc_filter_fw(regs, a, endstep);

    if (++regs->buffer.index == AUDIO_UNIT_BUFNUM)
      regs->buffer.index = 0;

    regs->out.amp = a;
  }

  return muldiv(outamp, amp, AUDIO_PER_NUM);
}

static int calc_frames(struct audio_data *audiodata, short *frames, int n)
{
  int i, ch, amp;
  struct player *player, **pp;
  struct samplestep stepend;
  struct aunitset *aunitset;

  if (audiodata->finished) {
    for (i = 0; i < n; i++) {
      frames[i] = 0;
      step_add(&audiodata->step, NULL, 1, audiodata->samplefreq);
    }
    return n;
  }

  step_add(&stepend, &audiodata->step, n, audiodata->samplefreq);

  for (pp = &audiodata->ready; *pp;) {
    player = *pp;

    if (step_diff(&stepend, &player->step, audiodata->samplefreq) < 0) {
      pp = &player->nextplayer;
      continue;
    }

    if (player->next) {
      *pp = player->next;
      (*pp)->nextplayer = player->nextplayer;
    } else {
      *pp = player->nextplayer;
    }

    player->nextplayer = NULL;

    player->next = audiodata->busy;
    audiodata->busy = player;
  }

  for (i = 0; i < n;) {
    amp = 0;
    for (pp = &audiodata->busy; *pp;) {
      player = *pp;

      step_add(&stepend, &player->step, player->steplen, audiodata->samplefreq);

      if (!(step_diff(&audiodata->step, &stepend, audiodata->samplefreq) < 0)) {
	*pp = player->next;
	aunit_regs_push(&player->head->pool, player->regs);
	player->regs = NULL;
	player->next = audiodata->complete;
	audiodata->complete = player;
	continue;
      }

      if (!(step_diff(&audiodata->step, &player->step, audiodata->samplefreq) < 0)) {
	if (!player->regs) {
	  aunitset = &aunitsets[player->aunitset_index];
	  player->regs = aunit_regs_pop(&player->head->pool, aunitset);
	}
	amp += aunit_regs_calc(player->regs, player->amp,
			       player->freq, audiodata->samplefreq,
			       step_diff(&audiodata->step, &player->step, audiodata->samplefreq),
			       player->stepbase, player->steplen);
      }
      pp = &player->next;
    }

#ifdef AUDIO_AVERAGING_AMP_LEVEL
    amp = audiodata->num ? (amp / audiodata->num) : 0;
#endif
    if (amp >  AMPLIMIT) amp =  AMPLIMIT;
    if (amp < -AMPLIMIT) amp = -AMPLIMIT;

    for (ch = 0; ch < audiodata->channels; ch++) {
      if (i < n)
	frames[i++] = amp;
    }

    step_add(&audiodata->step, NULL, 1, audiodata->samplefreq);
  }

  n = i;

  if (!audiodata->ready && !audiodata->busy)
    audiodata->finished = 1;

  return n;
}

#ifdef USE_SDL
static void sdl_callback(void *userdata, unsigned char *stream, int len)
{
  struct audio_data *audiodata;
  short *frames;
  int n;

  audiodata = (struct audio_data *)userdata;

  frames = (short *)stream;
  n = len / sizeof(short);

  calc_frames(audiodata, frames, n);
}

static int sdl_thread_main(void *p)
{
  struct audio_data *audiodata;
  SDL_AudioSpec spec;

  audiodata = (struct audio_data *)p;

  memset(&spec, 0, sizeof(spec));

  spec.freq = audiodata->samplefreq;
  spec.format = AUDIO_S16;
  spec.channels = audiodata->channels;
  spec.samples = audiodata->samples;
  spec.callback = sdl_callback;
  spec.userdata = audiodata;

#ifdef USE_SDL2
  SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
#endif

  SDL_OpenAudio(&spec, NULL);
  SDL_PauseAudio(0);
#if 0
  SDL_Delay((audiodata->length.sec * 1000) + muldiv(audiodata->length.count, 1000, audiodata->samplefreq));
#else
  while (!audiodata->finished && !nll_is_finished()) {
    SDL_Delay(10);
  }
#endif
  SDL_PauseAudio(1);
  SDL_CloseAudio();

  audiodata->finished = 1;

  return 0;
}

static int _sdl_run(struct audio_data *audiodata)
{
#ifdef USE_SDL1
  audiodata->thread = SDL_CreateThread(sdl_thread_main, audiodata);
#endif
#ifdef USE_SDL2
  audiodata->thread = SDL_CreateThread(sdl_thread_main, "SDL AUDIO thread", audiodata);
#endif

  return 0;
}
#endif

#ifdef AUDIO_EXTERNAL_DRIVER
static int external_callback(int channels, int samples, short *buffer, void *arg)
{
  calc_frames((struct audio_data *)arg, buffer, channels * samples);
  return 0;
}

static int external_run(struct audio_data *audiodata, unsigned int flags)
{
  if (ext_audio_run(audiodata->samplefreq, audiodata->channels, audiodata->samples, external_callback, audiodata) < 0)
    return -1;

  while (!audiodata->finished && !nll_is_finished()) {
    usleep(10000);
  }

  ext_audio_stop();

  audiodata->finished = 1;

  return 0;
}
#endif

static int _run(struct audio_data *audiodata, unsigned int flags)
{
#ifdef AUDIO_EXTERNAL_DRIVER
  external_run(audiodata, flags);
#else
#ifdef USE_SDL
  _sdl_run(audiodata);
#else
  audiodata->finished = 1;
#endif
#endif
  return 0;
}

static int _run_file(struct audio_data *audiodata, unsigned int flags, const char *filename)
{
  sound_t sound = NULL;
  int r, bits = 16, n, samplenum;
#define FRAMENUM 256
  short frames[FRAMENUM];
  sound_type_t type;

  samplenum = step_num(&audiodata->length, audiodata->samplefreq);

  type = (flags & NLL_A_TEXT) ? SOUND_TYPE_TEXT : SOUND_TYPE_WAV;

  sound = sound_create(type, flags);
  if (!sound)
    goto err;

  if ((r = sound_wopen(sound, filename, audiodata->samplefreq, audiodata->channels, bits, samplenum)) < 0)
    goto err;

  while (sound->offset < samplenum) {
    n = FRAMENUM / audiodata->channels;
    if (sound->offset + n >= samplenum)
      n = samplenum - sound->offset;
    calc_frames(audiodata, frames, n * audiodata->channels);
    sound_write(sound, frames, n);
    if (audiodata->finished)
      break;
  }

  if ((r = sound_wclose(sound)) < 0)
    goto err;

  sound_destroy(sound);

  audiodata->finished = 1;

  return 0;

err:
  if (sound)
    sound_destroy(sound);
  return -1;
}

static int player_aunit_regs_maxnum(struct player *player)
{
  struct aunitset *aunitset;
  int num = 0;

  for (; player; player = player->next) {
    if (!player->freq)
      continue;
    aunitset = &aunitsets[player->aunitset_index];
    if (num < aunitset->num)
      num = aunitset->num;
  }

  return num;
}

static struct player *player_run(struct player *player, struct samplestep *length, unsigned int flags, const char *filename)
{
  struct audio_data *audiodata;
  struct player *p, *ret = NULL;
  int num = 0;

  audiodata = audiodata_alloc();
  if (!audiodata)
    return player;

  if (flags & NLL_A_REMAIN) {
    ret = player;
    player = player_copy(player);
  }

  for (p = player; p; p = p->nextplayer) {
    num++;
    p->pool = aunit_regs_alloc_num(player_aunit_regs_maxnum(p) * (p->duplicate + 1));
  }

  audiodata->finished = 0;
  audiodata->num = num;
  audiodata->samplefreq = sampling_freq;
  audiodata->channels = !(flags & NLL_A_STEREO) ? 1 : 2;
  audiodata->samples = AUDIO_DEFAULT_SAMPLES;
  step_copy(&audiodata->length, length);
  step_clear(&audiodata->step);

  audiodata->ready    = player;
  audiodata->busy     = NULL;
  audiodata->complete = NULL;

  audiodata->next = audiodata_list;
  audiodata_list = audiodata;

  if (filename)
    _run_file(audiodata, flags, filename);
  else
    _run(audiodata, flags);

  audiodata_list_clean();

  return ret;
}

static int _aunitset_index = NLL_A_DEFUNITSET;

int audio_stop(void)
{
  init();
  audiodata_list_stop();

  return 0;
}

int audio_clear(int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *next;

  init();
  audiodata_list_stop();

  if (aunitset_index >= AUDIO_UNITSET_MAXNUM)
    return -1;

  if (aunitset_index < 0)
    return audio_clear_all();

  aunitset = &aunitsets[aunitset_index];

  player_free(playeres);
  playeres = NULL;

  for (; aunitset->aunits; aunitset->aunits = next) {
    next = aunitset->aunits->next;
    _aunit_free(aunitset->aunits);
  }

  aunitset->num = 0;

  return 0;
}

int audio_clear_all(void)
{
  int r, i;

  init();
  audiodata_list_stop();

  for (i = 0; i < AUDIO_UNITSET_MAXNUM; i++) {
    if ((r = audio_clear(i)) < 0)
      return r;
  }

  return 0;
}

int audio_getsamplefreq(void)
{
  init();
  audiodata_list_clean();

  return sampling_freq;
}

int audio_setsamplefreq(int samplefreq)
{
  init();
  audiodata_list_clean();

  if (samplefreq <= 0)
    samplefreq = AUDIO_DEFAULT_SAMPLING_FREQ;

  sampling_freq = samplefreq;

  return 0;
}

int audio_playnum(void)
{
  init();
  audiodata_list_clean();

  return audiodata_list_num();
}

int audio_wait(void)
{
  init();
  audiodata_list_clean();

  return audiodata_list_wait();
}

int audio_unitset(int aunitset_index)
{
  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = NLL_A_DEFUNITSET;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  _aunitset_index = aunitset_index;

  return 0;
}

int audio_unit(int output, int amp, int flags, int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *aunit;
  int r, n;

  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  aunitset = &aunitsets[aunitset_index];

  if ((aunit = _aunit_alloc()) == NULL)
    return -1;
  memset(aunit, 0, sizeof(*aunit));

  aunit->index = ++aunitset->num;
  aunit->flags = flags;

  aunit->osc.type = NLL_A_ZERO;
  aunit->osc.mod  = NLL_A_NULL;

  aunit->next = aunitset->aunits;
  aunitset->aunits = aunit;

  if (aunit->flags & NLL_A_ENVCLEAR) {
    aunit->envelope.num = 0;
  } else if (aunit->flags & NLL_A_ENVFDOUT) {
    n = 0;
    aunit->envelope.points[n  ].step = 0;
    aunit->envelope.points[n++].amp  = AUDIO_PER_NUM;
    aunit->envelope.points[n  ].step = AUDIO_PER_NUM;
    aunit->envelope.points[n++].amp  = 0;
    aunit->envelope.num = n;
  } else if (aunit->flags & NLL_A_ENVFDIN) {
    n = 0;
    aunit->envelope.points[n  ].step = 0;
    aunit->envelope.points[n++].amp  = 0;
    aunit->envelope.points[n  ].step = AUDIO_PER_NUM;
    aunit->envelope.points[n++].amp  = AUDIO_PER_NUM;
    aunit->envelope.num = n;
  }

  if (aunit->flags & NLL_A_FILCLEAR) {
    aunit->filter.forward.num = 0;
    aunit->filter.backward.num = 0;
  }

  if ((r = audio_addout(aunit->index, output, amp, flags, aunitset_index)) < 0) {
    aunitset->aunits = aunit->next;
    _aunit_free(aunit);
    return r;
  }

  return aunit->index;
}

int audio_osc(int index, int amp, int freq, int phase, int type, int mod, int flags, int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *aunit;

  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  aunitset = &aunitsets[aunitset_index];

  aunit = aunitset_search(aunitset, index);
  if (!aunit)
    return -1;

  aunit->osc.amp   = amp;
  aunit->osc.freq  = freq;
  aunit->osc.phase = phase;
  aunit->osc.type  = type;
  aunit->osc.mod   = mod;
  aunit->osc.flags = flags;

  if ((aunit->osc.type & NLL_A_TYPE_MASK) == NLL_A_EXTIN) {
    aunit->in.aunit = aunitset_search(aunitset, aunit->osc.type & ~NLL_A_TYPE_MASK);
  }

  return 0;
}

int audio_addout(int index, int output, int amp, int flags, int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *aunit, *out;

  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  aunitset = &aunitsets[aunitset_index];

  aunit = aunitset_search(aunitset, index);
  if (!aunit)
    return -1;

  if (aunit->outnum == AUDIO_UNIT_OUTMAX)
    return -1;

  if (output < 0)
    return 0;

  if (output == NLL_A_OUTPUT) {
    out = NULL;
  } else {
    out = aunitset_search(aunitset, output);
    if (!out)
      return -1;
  }

  aunit->outs[aunit->outnum  ].amp = amp;
  aunit->outs[aunit->outnum++].aunit = out;

  return 0;
}

int audio_envpoint(int index, int n, int step, int amp, int flags, int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *aunit;

  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  aunitset = &aunitsets[aunitset_index];

  aunit = aunitset_search(aunitset, index);
  if (!aunit)
    return -1;

  if (flags & NLL_A_ENVCLEAR) {
    aunit->envelope.num = 0;
    memset(aunit->envelope.points, 0, sizeof(aunit->envelope.points));
  }

  if (n < 0)
    n = aunit->envelope.num;

  if (n >= AUDIO_UNIT_ENVMAX)
    return -1;

  aunit->envelope.points[n].step  = step;
  aunit->envelope.points[n].amp   = amp;
  aunit->envelope.points[n].flags = flags;
  n++;

  if (aunit->envelope.num < n)
    aunit->envelope.num = n;

  return 0;
}

int audio_envcopy(int index, int source, int flags, int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *aunit, *saunit;
  int i;

  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  aunitset = &aunitsets[aunitset_index];

  aunit = aunitset_search(aunitset, index);
  if (!aunit)
    return -1;

  saunit = aunitset_search(aunitset, source);
  if (!saunit)
    return -1;

  for (i = 0; i < AUDIO_UNIT_ENVMAX; i++) {
    if (i < saunit->envelope.num) {
      aunit->envelope.points[i].step  = saunit->envelope.points[i].step;
      aunit->envelope.points[i].amp   = saunit->envelope.points[i].amp;
      aunit->envelope.points[i].flags = saunit->envelope.points[i].flags;
    } else {
      aunit->envelope.points[i].step  = 0;
      aunit->envelope.points[i].amp   = 0;
      aunit->envelope.points[i].flags = 0;
    }
  }

  aunit->envelope.num = saunit->envelope.num;

  return 0;
}

int audio_filpoint(int index, int type, int n, int amp, int flags, int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *aunit;

  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  aunitset = &aunitsets[aunitset_index];

  aunit = aunitset_search(aunitset, index);
  if (!aunit)
    return -1;

  switch (type) {
  case NLL_A_FILFW:
    if (flags & NLL_A_FILCLEAR) {
      aunit->filter.forward.num = 0;
      memset(aunit->filter.forward.points, 0, sizeof(aunit->filter.forward.points));
    }
    if (n < 0)
      n = aunit->filter.forward.num;
    if (n >= AUDIO_UNIT_FILFWMAX)
      return -1;
    aunit->filter.forward.points[n].amp   = amp;
    aunit->filter.forward.points[n].flags = flags;
    n++;
    if (aunit->filter.forward.num < n)
      aunit->filter.forward.num = n;
    break;

  case NLL_A_FILBW:
    if (flags & NLL_A_FILCLEAR) {
      aunit->filter.backward.num = 0;
      memset(aunit->filter.backward.points, 0, sizeof(aunit->filter.backward.points));
    }
    if (n < 0)
      n = aunit->filter.backward.num;
    if (n >= AUDIO_UNIT_FILBWMAX)
      return -1;
    aunit->filter.backward.points[n].amp   = amp;
    aunit->filter.backward.points[n].flags = flags;
    n++;
    if (aunit->filter.backward.num < n)
      aunit->filter.backward.num = n;
    break;

  default:
    return -1;
  }

  return 0;
}

int audio_filcopy(int index, int source, int flags, int aunitset_index)
{
  struct aunitset *aunitset;
  struct aunit *aunit, *saunit;
  int i;

  init();
  audiodata_list_clean();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  aunitset = &aunitsets[aunitset_index];

  aunit = aunitset_search(aunitset, index);
  if (!aunit)
    return -1;

  saunit = aunitset_search(aunitset, source);
  if (!saunit)
    return -1;

  for (i = 0; i < AUDIO_UNIT_FILFWMAX; i++) {
    if (i < saunit->filter.forward.num) {
      aunit->filter.forward.points[i].amp   = saunit->filter.forward.points[i].amp;
      aunit->filter.forward.points[i].flags = saunit->filter.forward.points[i].flags;
    } else {
      aunit->filter.forward.points[i].amp   = 0;
      aunit->filter.forward.points[i].flags = 0;
    }
  }

  aunit->filter.forward.num = saunit->filter.forward.num;

  for (i = 0; i < AUDIO_UNIT_FILBWMAX; i++) {
    if (i < saunit->filter.backward.num) {
      aunit->filter.backward.points[i].amp   = saunit->filter.backward.points[i].amp;
      aunit->filter.backward.points[i].flags = saunit->filter.backward.points[i].flags;
    } else {
      aunit->filter.backward.points[i].amp   = 0;
      aunit->filter.backward.points[i].flags = 0;
    }
  }

  aunit->filter.backward.num = saunit->filter.backward.num;

  return 0;
}

static int code2scale[] = { 0, 2, 4, 5, 7, 9, 11 };

int audio_play(const char *mml, int flags, int aunitset_index, const char *filename)
{
  static struct player **pp;
  int scale, octadd = 0, oct, len;
  int interval, steplen, dot;
  static int octave = 3, length = 4, amp = AUDIO_PER_NUM, tempo = 120, stepmod = 0;
  static struct samplestep step, max;
  int bracket = 0, bracketlen = 0, duplicate = 0;
  const char *s, *upper, *lower;
  char *p;

  init();
  audiodata_list_stop();

  if (aunitset_index == NLL_A_NULLUNITSET)
    aunitset_index = _aunitset_index;

  if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
    return -1;

  upper = "CDEFGAB";
  lower = "cdefgab";

  if (!playeres) {
    step_clear(&max);
  }

  if (!playeres || (flags & NLL_A_NEW)) {
    pp = &playeres;
    step_clear(&step);
    stepmod = 0;
    duplicate = 0;
  }

  for (s = mml; s && *s;) {
    switch (*s) {
    case ' ':
    case '\t':
    case '\r':
    case '\n':
      s++;
      break;

    case 'O':
      s++;
      if (*s && isdigit(*s)) {
	octave = *s - '0';
	s++;
      }
      break;

    case '<':
      s++;
      octadd--;
      break;

    case '>':
      s++;
      octadd++;
      break;

    case 'L':
      s++;
      if (*s && isdigit(*s)) {
	length = strtol(s, &p, 10);
	if (s == p)
	  goto err;
	s = p;
	length = length ? length : 4;
      }
      break;

    case 'V':
      s++;
      if (*s && isdigit(*s)) {
	amp = strtol(s, &p, 10);
	if (s == p)
	  goto err;
	s = p;
      }
      break;

    case 'T':
      s++;
      if (*s && isdigit(*s)) {
	tempo = strtol(s, &p, 10);
	if (s == p)
	  goto err;
	s = p;
	tempo = tempo ? tempo : 120;
      }
      break;

    case '@':
      s++;
      if (*s && isdigit(*s)) {
	aunitset_index = strtol(s, &p, 10);
	if (s == p)
	  goto err;
	s = p;
	if (aunitset_index == NLL_A_NULLUNITSET)
	  aunitset_index = NLL_A_DEFUNITSET;
	if ((aunitset_index < 0) || (aunitset_index >= AUDIO_UNITSET_MAXNUM))
	  return -1;
      }
      break;

    case '(':
      s++;
      if (bracket)
	goto err;
      bracketlen = 0;
      bracket = 1;
      break;

    case ')':
      s++;
      if (!bracket)
	goto err;
      step_add(&step, NULL, bracketlen, sampling_freq);
      bracket = 0;
      duplicate = 0;
      break;

    case 'R':
    case 'r':
    default:
      if ((*s == 'R') || (*s == 'r')) {
	scale = -1;
      } else if ((p = strchr(upper, *s)) != NULL) {
	scale = code2scale[p - upper];
      } else if ((p = strchr(lower, *s)) != NULL) {
	scale = code2scale[p - lower];
      } else {
	goto err;
      }
      s++;

      if (scale < 0) {
	oct = -1;
      } else {
	oct = octave + octadd;

	if (*s && ((*s == '#') || (*s == '+'))) {
	  scale++;
	  s++;
	}

	if (*s && (*s == '-')) {
	  scale--;
	  s++;
	}

	while (scale < 0) {
	  scale += SCALENUM;
	  oct--;
	}

	while (scale >= SCALENUM) {
	  scale -= SCALENUM;
	  oct++;
	}
      }

      len = length;
      if (*s && isdigit(*s)) {
	len = strtol(s, &p, 10);
	if (s == p)
	  goto err;
	s = p;
      }

      if (len <= 0)
	goto err;
      interval = 64 / len;

      dot = interval / 2;
      while (*s && (*s == '.')) {
	interval += dot;
	s++;
      }

#if 0
      msec = (1000 * 60 / 16) * interval / tempo;
      steplen = msec * sampling_freq / 1000;
#else /* To avoid the accumulation of the error, save the remainder */
      steplen = muldiv(60 * interval, sampling_freq, 16);
      steplen += stepmod; /* add the old remainder */
      stepmod = steplen % tempo; /* save the remainder */
      steplen = steplen / tempo;
#endif

      if (player_add(pp, aunitset_index, amp, scale, oct,
		     &step, muldiv(sampling_freq, 60, tempo), steplen) < 0)
	goto err;
      if (pp == &playeres) {
	(*pp)->nextplayer = (*pp)->next;
	(*pp)->next = NULL;
      }
      (*pp)->head = playeres;
      pp = &(*pp)->next;

      if (!bracket) {
	step_add(&step, NULL, steplen, sampling_freq);
      } else {
	if (bracketlen < steplen)
	  bracketlen = steplen;
	if (playeres->duplicate < duplicate)
	  playeres->duplicate = duplicate;
	duplicate++;
      }

      break;
    }
  }

  if (step_diff(&max, &step, sampling_freq) < 0)
    step_copy(&max, &step);

  if (!(flags & NLL_A_SAVE)) {
    playeres = player_run(playeres, &max, flags, filename);
  }

  return 0;

err:
  return -1;
}

int audio_open(char *filename, int *lengthp, unsigned int flags)
{
  sound_t sound = NULL;
  sound_type_t type;

  init();
  audiodata_list_stop();

  type = (flags & NLL_A_TEXT) ? SOUND_TYPE_TEXT : SOUND_TYPE_WAV;

  sound = sound_create(type, flags);
  if (!sound)
    goto err;

  if (sound_ropen(sound, filename) < 0)
    goto err;

  if (lengthp) *lengthp = muldiv(sound->samplenum, 1000, sound->samplefreq);

  return sound_get_index(sound);

err:
  if (sound)
    sound_destroy(sound);
  return -1;
}

int audio_close(int index)
{
  sound_t sound;

  init();
  audiodata_list_stop();

  sound = sound_get_sound(index);

  if (!sound)
    return -1;

  sound_destroy(sound);

  return index;
}
#endif
