/*
 * ---------------------------------------------------------------------------
 * sfxserver/mix.c
 *
 * Copyright by Terry Evans 1994
 * tevans@cs.utah.edu, tevans@slc.unisys.com
 * ---------------------------------------------------------------------------
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer. 2.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ---------------------------------------------------------------------------
 */

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


#include "global.h"
#include "types.h"
#include "channel.h"
#include "io.h"
#include "error.h"


/* From channel.c */
extern int channel_number;


void M_Init(mix_t *mix, int frag_size)
{
  IO_WriteStdout("        Setting up mix data structure\n");

  if((mix->clipped = (unsigned char *)calloc(sizeof(char), frag_size)) == NULL)
    E_FatalError("M_Init", "Unable to calloc clipped buffer");

  if((mix->silence = (unsigned char *)calloc(sizeof(char), frag_size)) == NULL)
    E_FatalError("M_Init", "Unable to calloc silence buffer");

  /* Set the silence to be silent */
  memset(mix->silence, 128, frag_size);

  mix->size = frag_size;
}


void M_DeInit(mix_t *mix)
{
  if(mix->clipped != NULL)
    free(mix->clipped);
}


void M_MixChannels(mix_t *mix, channel_t **channel, int frag_size, int s_fd, int stereo)
{
  int loop;
  int frag_offset;
  int num_channels_in_use;
  char data;

  /* This is so it isn't recalc'd at each iteration of the loop */
  int half_frag = frag_size >> 1;

  /* We are only doing a half a frag at a time */
  /* This is for stereo output */
  for(frag_offset = 0; frag_offset < half_frag; frag_offset++)
  {
    mix->unclipped_l = 0;
    mix->unclipped_r = 0;
    num_channels_in_use = 0;

    /* channel_number is the maximum channel number allocated so far */
    for(loop = 0; loop < channel_number; loop++)
    {
      /* See if it is a free channel */
      if(channel[loop] != NULL)
      {
	/* See if the channel is in use */
        if(channel[loop]->in_use)
        {
	  /* Normalize the data */
          data = (*(channel[loop]->sample->data + channel[loop]->position)) ^ 0x80;
	  mix->unclipped_l += channel[loop]->vol_left * (float)data;
	  mix->unclipped_r += channel[loop]->vol_right * (float)data;

	  num_channels_in_use++;

	  /* Increment to the next byte of sound to read in */
	  /* See if this sample is done being played */
	  if(++channel[loop]->position >= channel[loop]->sample->length)
	    channel[loop]->in_use = FALSE;
        }
      }
    }

    /* If there is nothing to play, just write out silence */
    if(num_channels_in_use == 0)
    {
      write(s_fd, mix->silence, frag_size);
      return;
    }

    /* Re-normalize the values */
    mix->unclipped_l += 128;
    mix->unclipped_r += 128;

    /* Left Channel */
    if(mix->unclipped_l < 0)
      mix->clipped[frag_offset << 1] = 0;
    else if(mix->unclipped_l > 255)
      mix->clipped[frag_offset << 1] = 255;
    else
      mix->clipped[frag_offset << 1] = mix->unclipped_l;

    /* Rigt Channel */
    if(mix->unclipped_r < 0)
      mix->clipped[(frag_offset << 1) + 1] = 0;
    else if(mix->unclipped_r > 255)
      mix->clipped[(frag_offset << 1) + 1] = 255;
    else
      mix->clipped[(frag_offset << 1) + 1] = mix->unclipped_r;
  }

  /* Write out the data */
  write(s_fd, mix->clipped, frag_size);
}
