#include "views.h"
#include <assert.h>
#include <sys/stat.h>

/* Delay the start of the spoken call by this many seconds from where I think */
/*  it should be */
#define CALL_DELAY	0
/* Hmm. That didn't work too well. Maybe long audio calls should not start quite*/
/*  as early */
#define CALL_MAX_BEFORE	3

void danceAudioHeader(FILE *afile) {
    /* Output placeholders for the wav header */
    int i;

    for ( i=0; i<44; ++i )
	putc('\0',afile);
}

static void danceAddSilence(DanceView *dv, int num_samples ) {
    int i;

    if ( num_samples<=0 )
return;
    for ( i=0; i<num_samples; ++i ) {
	putc('\0',dv->audio);
	putc('\0',dv->audio);
    }
    dv->audio_samples_out += num_samples;
}

static long danceFrameCntToAudioSamples(DanceView *dv, int frame) {
return( frame * (long) dv->audio_sample_rate / dv->frames_per_sec );
}

#define AUDIO_EOF	(-65536)
static int audioGetSample(FILE *audio,int bitsPerSample,int channels) {
    int ch1, ch2;
    int i;

    if ( bitsPerSample==8 ) {
	ch1 = getc(audio);
	if ( ch1==EOF )
return(AUDIO_EOF);
	/* Skip all channels but the first */
	for ( i=1; i<channels; ++i )
	    getc(audio);
return( (ch1-128)<<8 );
    } else {
	ch1 = getc(audio);
	ch2 = getc(audio);
	if ( ch2==EOF )
return(AUDIO_EOF);
	/* Skip all channels but the first */
	for ( i=1; i<channels; ++i ) {
	    getc(audio);
	    getc(audio);
	}
return( (short) (ch1|(ch2<<8)));
    }
}

struct wav_header {
    int bitsPerSample;
    int channels;
    int sampleRate;
    long numSamples;
};

/* parses the header of a WAV file and leaves the read pointer positioned at */
/*  the start of data (the first audio sample) */
static struct wav_header *audioFileReadHeader(FILE *audio, char *audioFilename, int minSampleRate, struct wav_header *hdr) {
    unsigned char buffer[44];
    int len=0;
    int bitsPerSample;
    int channels;
    int sampleRate;
    int numSamples = 0;

    memset(buffer,0,36);
    if ( audio==NULL )
return( NULL );

    len = fread((char *) buffer,1,36,audio);
    if ( !(len==36 && buffer[0]=='R' && buffer[1]=='I' && buffer[2]=='F' && buffer[3]=='F' &&
	    buffer[8]=='W' && buffer[9]=='A' && buffer[10]=='V' && buffer[11]=='E' &&
	    buffer[12]=='f' && buffer[13]=='m' && buffer[14]=='t' && buffer[15]==' ' &&
	    buffer[16]>=16 && buffer[17]==0 && buffer[18]==0 && buffer[19]==0 &&
	    buffer[20]==1 && buffer[21]==0 )) {
	fprintf( stderr, "Bad wav file: %s\n", audioFilename);
return( NULL );
    }

    bitsPerSample = buffer[34]|(buffer[35]<<8);
    channels      = buffer[22]|(buffer[23]<<8);
    sampleRate    = buffer[24]|(buffer[25]<<8)|(buffer[26]<<16)|(buffer[27]<<24);
    if ( bitsPerSample!=8 && bitsPerSample!=16 ) {
	fprintf( stderr, "Wav file with unsupported bits/sample=%d: %s\n", bitsPerSample, audioFilename);
return( NULL );
    } else if ( channels==0 ) {
	fprintf( stderr, "Wav file with no audio channels: %s\n", audioFilename);
return( NULL );
    } else if ( sampleRate<minSampleRate ) {
	fprintf( stderr, "Wav file with a sample rate below current requirement %d<%d: %s\n",
		sampleRate, minSampleRate, audioFilename);
return( NULL );
    }

    /* The fmt header may contain optional fields, if it does skip them */
    if ( buffer[16]>16 )
	fread(buffer,1,buffer[16]-16,audio);

    fread(buffer,1,8,audio);
    if ( !(buffer[0]=='d' && buffer[1]=='a' && buffer[2]=='t' && buffer[3]=='a' )) {
	fprintf( stderr, "Wav file missing data chunk: %s\n", audioFilename);
return( NULL );
    } else {
	numSamples = (buffer[4]|(buffer[5]<<8)|(buffer[6]<<16)|(buffer[7]<<24)) /
		(channels*bitsPerSample/8);
    }
    hdr->bitsPerSample = bitsPerSample;
    hdr->channels      = channels;
    hdr->sampleRate    = sampleRate;
    hdr->numSamples    = numSamples;
return( hdr );
}

static void danceAudioAddHeader(FILE *afile, int channels, int sample_rate) {
    long datalen;
    struct stat statb;

    fflush(afile);

    fstat(fileno(afile),&statb);
    datalen = statb.st_size - 44;

    rewind(afile);

    putc('R',afile);
    putc('I',afile);
    putc('F',afile);
    putc('F',afile);

    putc(((datalen+36)&0xff),afile);
    putc((((datalen+36)>>8)&0xff),afile);
    putc((((datalen+36)>>16)&0xff),afile);
    putc(((datalen+36)>>24),afile);

    putc('W',afile);
    putc('A',afile);
    putc('V',afile);
    putc('E',afile);

    putc('f',afile);
    putc('m',afile);
    putc('t',afile);
    putc(' ',afile);

    putc(16,afile);
    putc(0,afile);
    putc(0,afile);
    putc(0,afile);

    putc(1,afile);						/* PCM (raw) format */
    putc(0,afile);

    putc(channels,afile);
    putc(0,afile);

    putc((sample_rate&0xff),afile);				/* sample rate */
    putc(((sample_rate>>8)&0xff),afile);
    putc(((sample_rate>>16)&0xff),afile);
    putc((sample_rate>>24),afile);

    putc(((2*channels*sample_rate)&0xff),afile);		/* Byte rate (2 bytes/sample) */
    putc((((2*channels*sample_rate)>>8)&0xff),afile);
    putc((((2*channels*sample_rate)>>16)&0xff),afile);
    putc(((2*channels*sample_rate)>>24),afile);

    putc(2*channels,afile);					/* block align (sample size in bytes) */
    putc(0,afile);

    putc(16,afile);						/* bits per sample */
    putc(0,afile);

    putc('d',afile);
    putc('a',afile);
    putc('t',afile);
    putc('a',afile);

    putc(((datalen)&0xff),afile);
    putc((((datalen)>>8)&0xff),afile);
    putc((((datalen)>>16)&0xff),afile);
    putc(((datalen)>>24),afile);

    fflush( afile );
}

static void audioAddSamples(DanceView *dv,int max_samples, struct wav_header *hdr,
	FILE *audio, char *audioFilename ) {
    double pos=0, off;
    int sample1, sample0;
    int numSamples = hdr->numSamples;
    int oldMax = max_samples;

    off = hdr->sampleRate / (double) dv->audio_sample_rate;
    while ( max_samples>0 && numSamples>0 ) {
	sample1 = audioGetSample(audio,hdr->bitsPerSample,hdr->channels);
	if ( sample1==AUDIO_EOF )
    break;
	if ( pos<=0 ) {
	    if ( pos<0 )
		sample1 = ( (1+pos)*sample1 - pos*sample0 )/2;
	    putc( ( sample1    &0xff), dv->audio);
	    putc( ((sample1>>8)&0xff), dv->audio );
	    pos += off;
	    --max_samples;
	}
	sample0 = sample1;
	--pos;
	--numSamples;
    }
    if ( numSamples>0 )
	fprintf( stderr, "%s is too long for the time the call takes (left over=%.2f secs, max was %.2f)\n",
		audioFilename, numSamples / (double) hdr->sampleRate, oldMax/(double) hdr->sampleRate );
    dv->audio_samples_out += hdr->numSamples - numSamples;
}

/* This is called to add the title commentary to the audio track. */
/* it should start at once and involve no silence */
void danceAddAudioTitleFile(DanceView *dv,char *audioFilename) {
    int max_samples = danceFrameCntToAudioSamples(dv,dv->title_frame_offset);
    FILE *audio=NULL;
    struct wav_header header, *hdr;

    if ( audioFilename!=NULL && *audioFilename!='\0' ) {
	audio = fopen(audioFilename,"r");
	if ( audio==NULL )
	    fprintf( stderr, "Failed to open audio file: %s\n", audioFilename );
    }
    hdr = audioFileReadHeader(audio,audioFilename,dv->audio_sample_rate,&header);

    if ( hdr!=NULL )
	audioAddSamples(dv,max_samples,hdr,audio,audioFilename);
    if ( audio!=NULL )
	fclose(audio);
}

void danceAddAudioFile(DanceView *dv,char *audioFilename, int frame, double callSeconds) {
    long max_samples = ceil(callSeconds*dv->audio_sample_rate);
    long samplesStartVideoAction = danceFrameCntToAudioSamples(dv,dv->title_frame_offset+frame);
    long samplesInFile;
    FILE *audio=NULL;
    struct wav_header header, *hdr;

    if ( audioFilename!=NULL && *audioFilename!='\0' ) {
	audio = fopen(audioFilename,"r");
	if ( audio==NULL )
	    fprintf( stderr, "Failed to open audio file: %s\n", audioFilename );
    }
    hdr = audioFileReadHeader(audio,audioFilename,dv->audio_sample_rate,&header);

    if ( hdr!=NULL ) {
	samplesInFile = hdr->numSamples*dv->audio_sample_rate/hdr->sampleRate;
	if ( samplesInFile>max_samples )
	    samplesInFile = max_samples;

	if ( samplesStartVideoAction > dv->audio_samples_out )
	    danceAddSilence(dv,samplesStartVideoAction-dv->audio_samples_out);
	audioAddSamples(dv,max_samples,hdr,audio,audioFilename);
    }
    if ( audio!=NULL )
	fclose(audio);
}

/* This should be called at the start of a call. We won't let the audio for */
/*  this call last more than callSeconds. frame is the first frame of the call */
/* Now the trick is that we want to have the spoken text end when the first */
/*  video frame displays, to give the viewer time to react. That means the */
/*  audio will start somewhere in the previous action. If the previous action */
/*  is short then the audio may have to spill over into the current action */
/* Never let the time for the audio be longer than the time given for the */
/*  action, no matter when the audio starts */
void danceAddAudioCallFile(DanceView *dv,char *audioFilename, char *longFilename, int frame, double callSeconds) {
    long max_samples = ceil(callSeconds*dv->audio_sample_rate);
    long samplesStartVideoAction = danceFrameCntToAudioSamples(dv,dv->title_frame_offset+frame);
    long samplesInFile;
    long samplesStartAudio;
    FILE *audio=NULL, *longaudio=NULL;
    struct wav_header header, *hdr, longheader, *lhdr;

    if ( longFilename!=NULL && *longFilename!='\0' ) {
	longaudio = fopen(longFilename,"r");
	if ( longaudio==NULL )
	    fprintf( stderr, "Failed to open audio file: %s\n", longFilename );
    }
    lhdr = audioFileReadHeader(longaudio,longFilename,dv->audio_sample_rate,&longheader);

    if ( audioFilename!=NULL && *audioFilename!='\0' ) {
	audio = fopen(audioFilename,"r");
	if ( audio==NULL )
	    fprintf( stderr, "Failed to open audio file: %s\n", audioFilename );
    }
    hdr = audioFileReadHeader(audio,audioFilename,dv->audio_sample_rate,&header);

    if ( lhdr!=NULL ) {
	samplesInFile = lhdr->numSamples*dv->audio_sample_rate/lhdr->sampleRate;
	if ( samplesInFile>max_samples ) {
	    lhdr = NULL;
	    fclose(longaudio);
	} else {
	    hdr = lhdr;
	    if ( audio!=NULL )
		fclose(audio);
	    audio = longaudio;
	    audioFilename = longFilename;
	}
    } else if ( longaudio!=NULL )
	fclose(longaudio);

    if ( hdr!=NULL ) {
	samplesInFile = hdr->numSamples*dv->audio_sample_rate/hdr->sampleRate;
	if ( samplesInFile>max_samples )
	    samplesInFile = max_samples;
	if ( samplesInFile>CALL_MAX_BEFORE*dv->audio_sample_rate )
	    samplesInFile = CALL_MAX_BEFORE*dv->audio_sample_rate;
	samplesStartAudio = samplesStartVideoAction - samplesInFile;

	/* Sigh, if I do that, it feels too early, so I'll introduce a delay */
	samplesStartAudio += dv->audio_sample_rate*CALL_DELAY;

	if ( samplesStartAudio > dv->audio_samples_out )
	    danceAddSilence(dv,samplesStartAudio-dv->audio_samples_out);
	audioAddSamples(dv,max_samples,hdr,audio,audioFilename);
    }
    if ( audio!=NULL )
	fclose(audio);
}

/* Fill with silence up to frame, then fill in the header info, don't close the*/
/*  file (and don't set dv->audio to NULL, I use it as a flag), we may need */
/*  to merge it in with the music file */
void danceFinishAudio(DanceView *dv, int frame) {
    long final_samples = danceFrameCntToAudioSamples(dv, frame+dv->title_frame_offset);

    if ( final_samples > dv->audio_samples_out )
	danceAddSilence(dv,final_samples-dv->audio_samples_out);
    if ( final_samples != dv->audio_samples_out )
	fprintf( stderr, "Audio track is longer than video track.\n" );

    danceAudioAddHeader(dv->audio,1,dv->audio_sample_rate);
}

int danceHasAudio(Dance *dance) {
    int i;

    if ( dance->titleAudioFilename!=NULL && *dance->titleAudioFilename!='\0' )
return( true );
    for ( i=0; i<dance->call_cnt; ++i )
	if ( dance->calls[i].audioFilename!=NULL && *dance->calls[i].audioFilename!='\0' )
return( true );
    if ( dance->sequence!=NULL ) {
	for ( i=0; i<dance->choreo_cnt; ++i )
	    if ( danceHasAudio(dance->sequence[i].figure))
return( true );
    }

return( false );
}

/* This is to be called just before the normal speed minorsets commence */
void danceAddMusicFile(DanceView *dv, int frame) {
    long prepad_silent_samples;
    FILE *audio=NULL;
    struct wav_header header, *hdr;
    int i,c;

    long now   = (frame + dv->title_frame_offset) * (long) dv->audio_sample_rate / dv->frames_per_sec;

    audio = fopen(dv->dance->music_filename,"r");
    hdr = audioFileReadHeader(audio,dv->dance->music_filename,dv->audio_sample_rate,&header);
    if ( hdr==NULL ) {
	fclose(audio);
	fclose(dv->music);
	dv->music=NULL;
return;
    }

    dv->music_end_framecnt = frame + dv->title_frame_offset + /* Where we are now */
		(((double) hdr->numSamples)/hdr->sampleRate /*- dv->dance->music_leadin*/) * dv->frames_per_sec;
    prepad_silent_samples = now /*- (double) dv->dance->music_leadin * hdr->sampleRate*/;
    if ( prepad_silent_samples<0 ) {
	fprintf( stderr, "The music lead in is too long for the space we've got.\n" );
	fclose(dv->music);
	fclose(audio);
	dv->music=NULL;
return;
    }
    for ( i=0; i<prepad_silent_samples; ++i ) {
	for ( c=0; c<hdr->channels; ++c ) {
	    putc('\0', dv->music );
	    putc('\0', dv->music );
	}
    }

    for ( i=0; i<hdr->numSamples; ++i ) {
	for ( c=0; c<hdr->channels; ++c ) {
	    putc(getc(audio),dv->music);
	    putc(getc(audio),dv->music);
	}
    }
    danceAudioAddHeader(dv->music,hdr->channels,hdr->sampleRate);
}

static void danceAudioMixFiles(DanceView *dv,FILE *music,double amplify) {
    /* mix the spoken calls with the music into one file */
    FILE *output=NULL;
    char buffer[400], buffer2[400];
    struct wav_header spokenheader, musicheader, *shdr, *mhdr;
    int i;

    snprintf( (char *) buffer, sizeof(buffer), "%s/audioMix.wav", dv->framedir );
    output = fopen( (char *) buffer, "w+" );
    if ( output==NULL ) {
	fprintf( stderr, "Failed to open audio mix output file: %s\n", buffer );
exit(1);
    }
    danceAudioHeader(output);

    rewind(dv->audio);
    shdr = audioFileReadHeader(dv->audio,"audio.wav",dv->audio_sample_rate,&spokenheader);

    rewind(music);
    mhdr = audioFileReadHeader(music,dv->dance->music_filename,dv->audio_sample_rate,&musicheader);

    if ( shdr==NULL || mhdr==NULL ) {
	if ( shdr==NULL && mhdr==NULL )
	    fprintf( stderr, "Both spoken and music audio have a bad format\n" );
	else if ( shdr==NULL )
	    fprintf( stderr, "Spoken audio has a bad format\n" );
	else
	    fprintf( stderr, "Music audio has a bad format\n" );
exit(1);
    }
	
    if ( shdr->sampleRate != mhdr->sampleRate ) {
	fprintf( stderr, "Spoken and music audio have different sample rates %d %d\n", mhdr->sampleRate, shdr->sampleRate );
exit(1);
    }
    if ( mhdr->bitsPerSample!=16 || shdr->bitsPerSample!=16 ) {
	/* Can't happen */
	fprintf( stderr, "Unsupported bits per sample\n" );
exit(1);
    }
    if ( shdr->channels > 1 || mhdr->channels>2 ) {
	fprintf( stderr, "Unexpected number of channels when mixing %d %d\n", shdr->channels, mhdr->channels );
exit(1);
    }

    if ( shdr->channels == 1 && mhdr->channels == 1 ) {
	/* Mix two mono channels */
	int sample1, sample2;
	int ch1, ch2;

	for ( i=0; i<shdr->numSamples && i<mhdr->numSamples; ++i ) {
	    ch1 = getc(dv->audio);
	    ch2 = getc(dv->audio);
	    sample1 = (short) (ch1|(ch2<<8));
	    ch1 = getc(music);
	    ch2 = getc(music);
	    sample2 = (short) (ch1|(ch2<<8));
	    sample2 *= amplify;

	    sample2 += sample1;
	    if ( sample2>32767 ) sample2 = 32767;
	    else if ( sample2<-32768 ) sample2 = -32768;
	    putc((sample2&0xff),output);
	    putc(((sample2>>8)&0xff),output);
	}
	while ( i<shdr->numSamples ) {
	    putc(getc(dv->audio),output);
	    putc(getc(dv->audio),output);
	    ++i;
	}
	while ( i<mhdr->numSamples ) {
	    ch1 = getc(dv->audio);
	    ch2 = getc(dv->audio);
	    sample2 = (short) (ch1|(ch2<<8));
	    sample2 *= amplify;
	    putc((sample2&0xff),output);
	    putc(((sample2>>8)&0xff),output);
	    ++i;
	}
    } else if ( shdr->channels == 1 && mhdr->channels == 2 ) {
	int sample1, sample2, sample3;
	int ch1, ch2;

	for ( i=0; i<shdr->numSamples && i<mhdr->numSamples; ++i ) {
	    ch1 = getc(dv->audio);
	    ch2 = getc(dv->audio);
	    sample1 = (short) (ch1|(ch2<<8));
	    ch1 = getc(music);
	    ch2 = getc(music);
	    sample2 = (short) (ch1|(ch2<<8));
	    ch1 = getc(music);
	    ch2 = getc(music);
	    sample3 = (short) (ch1|(ch2<<8));
	    sample2 *= amplify;
	    sample3 *= amplify;

	    sample2 += sample1;
	    if ( sample2>65535 ) sample2 = 65535;
	    else if ( sample2<-65536 ) sample2 = -65536;
	    putc((sample2&0xff),output);
	    putc(((sample2>>8)&0xff),output);
	    sample3 += sample1;
	    if ( sample3>65535 ) sample3 = 65535;
	    else if ( sample3<-65536 ) sample3 = -65536;
	    putc((sample3&0xff),output);
	    putc(((sample3>>8)&0xff),output);
	}
	while ( i<shdr->numSamples ) {
	    putc(getc(dv->audio),output);
	    putc(getc(dv->audio),output);
	    ++i;
	}
	while ( i<mhdr->numSamples ) {
	    ch1 = getc(dv->audio);
	    ch2 = getc(dv->audio);
	    sample2 = (short) (ch1|(ch2<<8));
	    sample2 *= amplify;
	    putc((sample2&0xff),output);
	    putc(((sample2>>8)&0xff),output);
	    ch1 = getc(dv->audio);
	    ch2 = getc(dv->audio);
	    sample2 = (short) (ch1|(ch2<<8));
	    sample2 *= amplify;
	    putc((sample2&0xff),output);
	    putc(((sample2>>8)&0xff),output);
	    ++i;
	}
    }
    danceAudioAddHeader(output,mhdr->channels,mhdr->sampleRate);
    snprintf( (char *) buffer2, sizeof(buffer2), "%s/audio.wav", dv->framedir );
    rename(buffer,buffer2); /* Rename new file to audio.wav */
    fclose(dv->audio); fclose(music);
    dv->audio = output;
}

void danceAudioMix(DanceView *dv) {
    danceAudioMixFiles(dv,dv->music,dv->dance->music_amplify);
}

double audioFileSeconds(char *filename) {
    FILE *audio=NULL;
    struct wav_header header, *hdr;

    if ( filename==NULL )
return( 0 );
    audio = fopen(filename,"r");
    if ( audio==NULL )
return( 0 );

    hdr = audioFileReadHeader(audio,filename,0,&header);
    fclose(audio);
    if ( hdr==NULL )
return( 0 );

return( hdr->numSamples/(double) hdr->sampleRate );
}

int danceExtraFramesForFile(DanceView *dv,char *filename,int frame, double maxSeconds) {
    double secondsForFile = audioFileSeconds(filename);
    long samplesForFile;
    long samplesExtra;
    double secondsExtra;

    if ( secondsForFile>maxSeconds )
	secondsForFile = maxSeconds;
    samplesForFile = secondsForFile*dv->audio_sample_rate;
    if ( samplesForFile>CALL_MAX_BEFORE*dv->audio_sample_rate )
	samplesForFile = CALL_MAX_BEFORE*dv->audio_sample_rate;
    samplesExtra   = samplesForFile + dv->audio_samples_out - CALL_DELAY*dv->audio_sample_rate
	    - danceFrameCntToAudioSamples(dv,frame+dv->title_frame_offset);
    if ( samplesExtra<=0 )
return( 0 );

    secondsExtra = samplesExtra / (double) dv->audio_sample_rate;
return( ceil( secondsExtra * dv->frames_per_sec ) );
}

static char *numberAudio[]= {
    "AudioCalls/one.wav",
    "AudioCalls/two.wav",
    "AudioCalls/three.wav",
    "AudioCalls/four.wav",
    "AudioCalls/five.wav",
    "AudioCalls/six.wav",
    "AudioCalls/seven.wav",
    "AudioCalls/eight.wav",
    "AudioCalls/nine.wav",
    "AudioCalls/ten.wav",
    "AudioCalls/eleven.wav",
    "AudioCalls/twelve.wav",
    NULL
};

static char *countName(DanceView *dv, int count) {

    if ( dv->add_tones_to_audio )
return( "AudioCalls/drop.wav" );

    if ( dv->dance->counts_per_bar==2 ) {
	count %= 8;
return( numberAudio[count] );
    } else {
	count %= 12;
	if ( count%3==0 )
return( numberAudio[count/3] );
	else
return( numberAudio[count%3] );
    }
}

/* We have two ways of counting at the moment */
/* if counts_per_bar==2 then we count up to eight and start over */
/* if counts_per_bar==3 then we count 1-2-3, 2-2-3, 3-2-3, 4-2-3 and start over */
/* Number of bars must be divisible by 4, length of a count (bar/2 or bar/3) */
/*   must be at least .5 seconds or there isn't time for a number */
/* And the sample rate for output must be 44100 (sample rate of numbers) */
void danceAudioAddCounts(DanceView *dv) {
    FILE *output=NULL, *number;
    char buffer[400], buffer2[400];
    struct wav_header numberheader, *nhdr;
    long samples_out;
    int count, countMax;
    int phrase = 4*dv->dance->counts_per_bar;
    int sample_rate = dv->audio_sample_rate;
    int samples_per_count = sample_rate
	    *dv->frames_per_bar
	    /dv->frames_per_sec
	    /dv->dance->counts_per_bar;
    FILE *oldAudio = dv->audio;

printf( "Samples per count = %d\n", samples_per_count );
    if (( dv->dance->counts_per_bar!=2 && dv->dance->counts_per_bar!=3 ) ||
	    danceMinorSetMillibarCount(dv->dance)%4000!=0 ||
	    dv->frames_per_bar / (double) dv->frames_per_sec /(double) dv->dance->counts_per_bar < .5 ||
	    sample_rate != 44100 )
return;

    snprintf( (char *) buffer, sizeof(buffer), "%s/audioCounts.wav", dv->framedir );
    output = fopen( (char *) buffer, "w+" );
    if ( output==NULL ) {
	fprintf( stderr, "Failed to open audio count output file: %s\n", buffer );
exit(1);
    }
    danceAudioHeader(output);
    samples_out = 0;
    dv->audio = output;
    if ( dv->fc_start_slowset<dv->fc_end_slowset ) {
	int samples_per_count = sample_rate
		*dv->fpb_slowset
		/dv->frames_per_sec
		/dv->dance->counts_per_bar;
	danceAddSilence(dv,dv->fc_start_slowset*sample_rate/dv->frames_per_sec);
	samples_out = dv->fc_start_slowset*sample_rate/dv->frames_per_sec;
	count = 0;
	countMax = (dv->fc_end_slowset-dv->fc_start_slowset)*dv->dance->counts_per_bar /
		dv->fpb_slowset;
	/* countMax should be a multiple of either 8 or 12. If rounding errors*/
	/*  get in the way, fix them */
	countMax = (countMax + phrase/2)/phrase;
	countMax *= phrase;
	while ( count<countMax ) {
	    number = fopen( countName(dv,count),"r" );
	    nhdr = audioFileReadHeader(number,countName(dv,count),sample_rate,&numberheader);
	    if ( nhdr->numSamples>= samples_per_count )
		fprintf( stderr, "%s is too long and will be cropped.\n", countName(dv,count));
	    else if ( nhdr->numSamples < samples_per_count )
		danceAddSilence(dv,samples_per_count-nhdr->numSamples);
	    audioAddSamples(dv,samples_per_count,nhdr,number,countName(dv,count));
	    samples_out += samples_per_count;
	    fclose(number);
	    ++count;
	}
    }

    danceAddSilence(dv,dv->fc_start_normal*sample_rate/dv->frames_per_sec-samples_out);
    samples_out = dv->fc_start_normal*sample_rate/dv->frames_per_sec;
    count = 0;
    countMax = (dv->fc_end_normal-dv->fc_start_normal)*dv->dance->counts_per_bar /
	    dv->frames_per_bar;
    /* countMax should be a multiple of either 8 or 12. If rounding errors*/
    /*  get in the way, fix them */
    countMax = (countMax + phrase/2)/phrase;
    countMax *= phrase;
    while ( count<countMax ) {
	number = fopen( countName(dv,count),"r" );
	nhdr = audioFileReadHeader(number,countName(dv,count),sample_rate,&numberheader);
	if ( nhdr->numSamples>= samples_per_count )
	    fprintf( stderr, "%s is too long and will be cropped.\n", countName(dv,count));
	else if ( nhdr->numSamples < samples_per_count )
	    danceAddSilence(dv,samples_per_count-nhdr->numSamples);
	audioAddSamples(dv,samples_per_count,nhdr,number,countName(dv,count));
	samples_out += samples_per_count;
	fclose(number);
	++count;
    }
    danceAudioAddHeader(output,1,dv->audio_sample_rate);
    if ( oldAudio==NULL ) {
	snprintf( (char *) buffer2, sizeof(buffer2), "%s/audio.wav", dv->framedir );
	rename(buffer,buffer2); /* Rename new file to audio.wav */
return;
    }
    dv->audio = oldAudio;
    danceAudioMixFiles(dv,output,dv->add_tones_to_audio ? .5 : .25);
    /* The above mixes dv->audio and output, closes both files, renames new file to audio, and sets dv->audio to it */
}

int danceHasClaps(Dance *dance) {
    int i;

    if ( dance->call_cnt==0 )
return( false );

    for ( i=0; i<dance->call_cnt; ++i )
	if ( dance->calls[i].claplist_millibars!=NULL &&
		dance->calls[i].claplist_millibars[0]!=CLAP_DONE )
return( true );

return( false );
}

/* length of a count (bar/2 or bar/3) */
/*   must be at least .5 seconds or there isn't time for a clap */
/* And the sample rate for output must be 44100 (sample rate of clap) */
void danceAudioAddClaps(DanceView *dv) {
    FILE *output=NULL, *clap;
    char buffer[400], buffer2[400];
    struct wav_header clapheader, *chdr;
    int sample_rate = dv->audio_sample_rate;
    long samples_per_count = sample_rate
	    *dv->frames_per_bar
	    /dv->frames_per_sec
	    /dv->dance->counts_per_bar;
    long samples_per_bar = sample_rate
	    *dv->frames_per_bar
	    /dv->frames_per_sec;
    long samples_per_dance = (long) sample_rate * danceMinorSetMillibarCount(dv->dance)
	    *dv->frames_per_bar
	    /dv->frames_per_sec
	    / 1000;
    int frames_per_dance = danceMinorSetMillibarCount(dv->dance) *dv->frames_per_bar / 1000;
    FILE *oldAudio = dv->audio;
    Dance *dance = dv->dance;
    int part;
    int millibars;
    long samples_out;
    long samples_from_start_minorset;
    int i,j,k;
    long clap_file_start;

    if ( !danceHasClaps(dv->dance) )
return;

printf( "Samples per bar = %ld, samples per dance=%ld\n", samples_per_bar, samples_per_dance );
    if ( dv->frames_per_bar / (double) dv->frames_per_sec /(double) dv->dance->counts_per_bar < .5 ||
	    sample_rate != 44100 )
return;
    /* Bug! This doesn't handle snap_not_clap, stamp_not_clap */
    clap = fopen( "AudioCalls/clap.wav","r" );
    chdr = audioFileReadHeader(clap,"AudioCalls/clap.wav",sample_rate,&clapheader);
    if ( clap==NULL ) {
	fprintf( stderr, "Cannot find clap audio file.\n" );
return;
    } else if ( chdr->numSamples>= samples_per_count )
	fprintf( stderr, "clap.wav is too long and will be cropped.\n" );
    clap_file_start = ftell(clap);

    snprintf( (char *) buffer, sizeof(buffer), "%s/audioClaps.wav", dv->framedir );
    output = fopen( (char *) buffer, "w+" );
    if ( output==NULL ) {
	fprintf( stderr, "Failed to open clap output file: %s\n", buffer );
exit(1);
    }
    danceAudioHeader(output);
    samples_out = 0;
    part = 0;
    dv->audio = output;

    if ( dv->fc_start_slowset<dv->fc_end_slowset ) {
	long samples_per_count = sample_rate
		*dv->fpb_slowset
		/dv->frames_per_sec
		/dv->dance->counts_per_bar;
	long samples_per_bar = sample_rate
		*dv->fpb_slowset
		/dv->frames_per_sec;
	danceAddSilence(dv,dv->fc_start_slowset*sample_rate/dv->frames_per_sec);
	samples_out = dv->fc_start_slowset*sample_rate/dv->frames_per_sec;

	samples_from_start_minorset = 0;
	millibars = 0;
	for ( i=0; i<dance->call_cnt; ++i ) {
	    CallList *cl = &dance->calls[i];
	    if ( !(cl->part_mask&(1<<part)) )
	continue;
	    if ( cl->claplist_millibars!=NULL ) {
		for ( j=0; cl->claplist_millibars[j]!=CLAP_DONE; ++j ) {
		    fseek( clap, clap_file_start, SEEK_SET);
		    if ( samples_from_start_minorset!=
			    (millibars+cl->claplist_millibars[j])*samples_per_bar/1000 ) {
			danceAddSilence(dv,
				(millibars+cl->claplist_millibars[j])*
			        samples_per_bar/1000L-samples_from_start_minorset );
			samples_from_start_minorset = (millibars+cl->claplist_millibars[j])*
			        samples_per_bar/1000L;
		    }
		    audioAddSamples(dv,samples_per_count,chdr,clap,"AudioCalls/clap.wav");
		    samples_from_start_minorset += chdr->numSamples;		    
		}
	    }
	    millibars += cl->millibars;
	}
	samples_out += samples_from_start_minorset;
	if ( ++part >= dance->part_cnt )
	    part = 0;
    }

/*    danceAddSilence(dv,dv->fc_start_normal*sample_rate/dv->frames_per_sec-samples_out); */
/*    samples_out = dv->fc_start_normal*sample_rate/dv->frames_per_sec; */
    for ( k=0; dv->fc_start_normal+k*frames_per_dance < dv->fc_end_normal-4; ++k ) {
	danceAddSilence(dv,dv->fc_start_normal*sample_rate/dv->frames_per_sec+k*samples_per_dance-samples_out);
	samples_out = dv->fc_start_normal*sample_rate/dv->frames_per_sec + k*samples_per_dance;
	samples_from_start_minorset = 0;
	millibars = 0;
	for ( i=0; i<dance->call_cnt; ++i ) {
	    CallList *cl = &dance->calls[i];
	    if ( !(cl->part_mask&(1<<part)) )
	continue;
	    if ( cl->claplist_millibars!=NULL ) {
		for ( j=0; cl->claplist_millibars[j]!=CLAP_DONE; ++j ) {
		    fseek( clap, clap_file_start, SEEK_SET);
		    if ( samples_from_start_minorset!=
			    (millibars+cl->claplist_millibars[j])*samples_per_bar/1000 ) {
			danceAddSilence(dv,
				(millibars+cl->claplist_millibars[j])*
			        samples_per_bar/1000L-samples_from_start_minorset );
			samples_from_start_minorset = (millibars+cl->claplist_millibars[j])*
			        samples_per_bar/1000L;
		    }
		    audioAddSamples(dv,samples_per_count,chdr,clap,"AudioCalls/clap.wav");
		    samples_from_start_minorset += chdr->numSamples;		    
		}
	    }
	    millibars += cl->millibars;
	}
	samples_out += samples_from_start_minorset;
	if ( ++part >= dance->part_cnt )
	    part = 0;
    }
    fclose(clap);
    
    danceAddSilence(dv,dv->fc_start_normal*sample_rate/dv->frames_per_sec+k*samples_per_dance-samples_out);

    danceAudioAddHeader(output,1,dv->audio_sample_rate);
    if ( oldAudio==NULL ) {
	snprintf( (char *) buffer2, sizeof(buffer2), "%s/audio.wav", dv->framedir );
	rename(buffer,buffer2); /* Rename new file to audio.wav */
return;
    }
    dv->audio = oldAudio;
    danceAudioMixFiles(dv,output, 1);
    /* The above mixes dv->audio and output, closes both files, renames new file to audio, and sets dv->audio to it */
}

void danceAudioAppend(DanceView *dv) {
    char buffer[400], buffer2[400];
    FILE *old;
    struct wav_header header, *hdr;
    struct wav_header oheader, *ohdr;
    int ch;

    sprintf( buffer, "%s/audio.wav", dv->framedir );
    sprintf( buffer2, "%s/oldaudio.wav", dv->framedir );

    if ( access(buffer,R_OK)==-1 )
return;
    if ( access(buffer2,R_OK)==-1 )
return;
    old = fopen(buffer2,"r+");
    if ( old==NULL )
return;
    ohdr = audioFileReadHeader(old,buffer2,0,&oheader);
    if ( ohdr==NULL ) {
	fclose(old);
return;
    }
    fseek(dv->audio,0L,SEEK_SET);
    hdr = audioFileReadHeader(dv->audio,buffer,0,&header);
    if ( hdr->sampleRate!=ohdr->sampleRate || hdr->channels!=ohdr->channels ) {
	fprintf( stderr, "Failed to append audio due to sampleRate or channel missmatch.\n" );
	fclose(old);
return;
    }
    fseek(old,0L,SEEK_END);
    while ( (ch=getc(dv->audio))!=EOF )
	putc(ch,old);
    fclose(dv->audio);
    fseek(old,0L,SEEK_SET);
    danceAudioAddHeader(old,ohdr->channels,ohdr->sampleRate);
    unlink(buffer);
    rename(buffer,buffer2);
    dv->audio = old;
}
    
    
/* stuff from danceAV for testing */
double danceMusicGuessCountsPerSecond(Dance *dance) {
    MusicSegment *ms;
    int time, bars, counts;

    if ( dance->music_filename==NULL )
return( 0 );

    ms = dance->music_minorset;
    if ( ms==NULL )
	ms = dance->music_body;
    time = bars = 0;
    while ( ms!=NULL ) {
	if ( ms->duration_millibars!=0 ) {
	    time += ms->end - ms->start;
	    bars += ms->duration_millibars;
	}
	ms = ms->next;
    }
    if ( bars==0 )
return( 0 );

    counts = bars * dance->counts_per_bar;

return( 60 * (double) counts / (double) time );
}
