/** @file patest_toomanysines.c
	@ingroup test_src
	@brief Play more sine waves than we can handle in real time as a stress test.
	@author Ross Bencina <rossb@audiomulch.com>
	@author Phil Burk <philburk@softsynth.com>
*/
/*
 * $Id: patest_toomanysines.c 1097 2006-08-26 08:27:53Z rossb $
 *
 * This program uses the PortAudio Portable Audio Library.
 * For more information see: http://www.portaudio.com
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * The text above constitutes the entire PortAudio license; however, 
 * the PortAudio community also makes the following non-binding requests:
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version. It is also 
 * requested that these non-binding requests be included along with the 
 * license above.
 */

#include <stdio.h>
#include <math.h>
#include "portaudio.h"

#define MAX_SINES     (500)
#define MAX_LOAD      (1.2)
#define SAMPLE_RATE   (44100)
#define FRAMES_PER_BUFFER  (512)
#ifndef M_PI
#define M_PI  (3.14159265)
#endif
#define TWOPI (M_PI * 2.0)

#define NCLIPS (16)
#define CLIPLEN (2*44100) 

struct sound_clip {
	int active;
	int nsamples;
	int pos;
	double *sample;
} clip[NCLIPS];


int nclips = 0;

/* precompute 16 2-second clips of various sine waves */
int init_clips()
{
	int i, j;
	double phase, phaseinc, sawinc, sawval;
	phaseinc = 0.01;
	sawinc = 0.01;

	for (i=0;i<NCLIPS;i++) {
		clip[i].nsamples = CLIPLEN;
		clip[i].pos = 0;
		clip[i].active = 0;
		sawval = 0;
		sawinc = (double) 0.01 + (double) i / 1000.0;
		phase = 0.0;
		clip[i].sample = malloc(sizeof(clip[i].sample[0]) * CLIPLEN); 
		if (clip[i].sample == NULL) {
			printf("malloc failed, i=%d\n", i);
			continue;
		}
		for (j=0;j<CLIPLEN;j++) {
			if ((i % 3) != 0) {
				clip[i].sample[j] = (double) (((CLIPLEN) - j) / ((double) 2*j+CLIPLEN)) * sin(phase);
				phase += phaseinc;
				if (phase > TWOPI) 
					phase -= TWOPI;
			} else {
				sawval += sawinc;
				clip[i].sample[j] = sawval;
				if (sawval > 0.80 || sawval < -0.80)
					sawinc = -sawinc;
			}
		}
		phaseinc *= 1.4;
		//phaseinc *= 1.02;
	}
}

typedef struct paTestData /* not even using this cookie... */
{
    int numSines;
    double phases[MAX_SINES];
}
paTestData;

/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback(const void *inputBuffer, void *outputBuffer,
	unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
	PaStreamCallbackFlags statusFlags, void *userData )
{
	paTestData *data = (paTestData*)userData;
	float *out = (float*)outputBuffer;
	unsigned long i;
	int j, k;
	int finished = 0;
	(void) inputBuffer; /* Prevent unused variable warning. */
	float output = 0.0;
	int sample;
	int count = 0;
	

	for (i=0; i<framesPerBuffer; i++) {
		output = 0.0;
		count = 0;
		for (j=0; j<NCLIPS; j++) {
			if (!clip[j].active)
				continue;
			sample = i + clip[j].pos;
			count++;
			if (sample >= clip[j].nsamples) {
				clip[j].active = 0;
				continue;
			}
			output += clip[j].sample[sample];
		}
		*out++ = (float) (output / count);
        }
	for (i=0;i<NCLIPS;i++) {
		if (!clip[i].active)
			continue;
		clip[i].pos += framesPerBuffer;
		if (clip[i].pos >= clip[i].nsamples)
			clip[i].active = 0;
	}
	return finished;
}

/*******************************************************************/
int main(void);
int main(void)
{
    PaStreamParameters outputParameters;
    PaStream *stream;
    PaError err;
    int numStress;
    paTestData data = {0};
    double load;
	int i,j ;

	init_clips();

    printf("Mixing %d clips of sound\n", NCLIPS);

    err = Pa_Initialize();
    if( err != paNoError ) goto error;
    
    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
    outputParameters.channelCount = 1;                      /* mono output */
    outputParameters.sampleFormat = paFloat32;              /* 32 bit floating point output */
    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
    outputParameters.hostApiSpecificStreamInfo = NULL;

    err = Pa_OpenStream(
              &stream,
              NULL,         /* no input */
              &outputParameters,
              SAMPLE_RATE,
              FRAMES_PER_BUFFER,
              paClipOff,    /* we won't output out of range samples so don't bother clipping them */
              patestCallback,
              &data );    
    if( err != paNoError ) goto error;
    err = Pa_StartStream( stream );
    if( err != paNoError ) goto error;

	for (i=0;i<20;i++) {
		for (j=0;j<NCLIPS;j++) {
			// printf("clip[%d].pos = %d, active = %d\n", j, clip[j].pos, clip[j].active);
			Pa_Sleep( 250 );
			if (clip[j].active == 0) {
				clip[j].nsamples = CLIPLEN;
				clip[j].pos = 0;
				clip[j].active = 1;
			}
		}
		Pa_Sleep( 1500 );
	}
    
    printf("Stop stream.\n");
    err = Pa_StopStream( stream );
    if( err != paNoError ) goto error;
    
    err = Pa_CloseStream( stream );
    if( err != paNoError ) goto error;
    
    Pa_Terminate();
    printf("Test finished.\n");
    return err;
error:
    Pa_Terminate();
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
    return err;
}
