/* (c)Copyright 1996-2000 NTT Cyber Space Laboratories */
/*                Released on 2000.09.06 by N. Iwakami */
/*                Modified on 2000.09.22 by N. Iwakami */
/*                Modified on 2000.10.03 by N. Iwakami, simplified version. */
/*                Thanks to Mr. Andy Philpotts for his suggestion.          */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "twinvq.h"
#include "bfile.h"
#include "bstream_e.h"
#include "tvqenc.h"
#include "ChunkHelper.h"

/*----------------------------------------------------------------------------*/
/* Definitions                                                                */
/*----------------------------------------------------------------------------*/
static const int YES = 1;
static const int NO  = 0;

static const int INIT_ERR_DISP_MBOX = YES;  /* YES: displays a message box when an initialization error occurs */

/*----------------------------------------------------------------------------*/
/* Name:        buftofr()                                                     */
/* Description: translate input audio data to TwinVQ frame data               */
/* Return:      none                                                          */
/* Access:      static                                                        */
/*----------------------------------------------------------------------------*/
static
void buftofr(int    blen,      /* Input  --- read data length */
			 short  bufin[],   /* Input  --- input data buffer array */
			 float in[],      /* Output --- output data frame */
			 int	frameSize,
			 int	numChannels)
{
	/*--- Variables ---*/
	int	ismp, ich, blen_ch;
	
	/*--- Initialization ---*/
	blen_ch = blen/numChannels;
	/*--- Main operation ---*/
	for ( ich=0; ich<numChannels; ich++ ){
		for ( ismp=0; ismp<blen_ch; ismp++ ){
			in[ich*frameSize+ismp] = (float)bufin[ismp*numChannels+ich];
		}
		for (ismp=blen_ch; ismp<frameSize; ismp++){
			in[ich*frameSize+ismp] = 0.;
		}
	}
}

/*============================================================================*/
/* Name:        main()                                                        */
/* Description: The main program                                              */
/* Return:      (int) returns 1 when error or returns 0                       */
/* Access:      external                                                      */
/*============================================================================*/
int main( int argc, char *argv[] )
{
	if (argc < 3) {
		fprintf( stderr, "Usage: %s <input PCM file> <output bitstream file>.\n", argv[0] );
		return 1;
	}
	// Parameter setup.
	headerInfo      setupInfo;
	encSpecificInfo encInfo;
	
	memset( &setupInfo, 0, sizeof( headerInfo) );
	memset( &encInfo, 0, sizeof( encSpecificInfo) );
	
	TvqGetVersionID( V2, setupInfo.ID ); // Version 2
	setupInfo.channelMode = 1;           // Stereo (0 for mono)
	setupInfo.samplingRate = 44;         // 44.1K
	setupInfo.bitRate = 96;              // 96 kbit/s
	
	encInfo.N_CAN_GLOBAL = 16;           // number of VQ pre-selection candidates
	
	strncpy(setupInfo.Name, "MySongTitle", BUFSIZ);
	strncpy(setupInfo.Comt, "MyComment", BUFSIZ);
	strncpy(setupInfo.Auth, "TheAuthor", BUFSIZ);
	strncpy(setupInfo.Cpyr, "copyright NTT", BUFSIZ);
	strncpy(setupInfo.File, "filename", BUFSIZ);

	// Initialize encoder
	INDEX index;
	int errorCode = TvqEncInitialize( &setupInfo, &encInfo, &index, INIT_ERR_DISP_MBOX);
	if (errorCode) {
		return 1;
	}

	// Open files
	FILE*  fp = NULL;
	BFILE* bfp = NULL;
	if ( (fp = fopen(argv[1], "rb")) == NULL ) {
		fprintf( stderr, "%s: Input audio file open error.\n", argv[1] );
		return 1;
	}
	if ( (bfp = bopen( argv[2],"wb")) == NULL ) {
		fprintf( stderr, "%s: Output bitstream file open error.\n", argv[1] );
		return 1;
	}

	// Initialize bitstream writer
	TvqInitBsWriter();

	// Initialize data buffer
	int frameSize      = TvqEncGetFrameSize();
	int numChannels    = TvqEncGetNumChannels();
	int totalFrameSize = frameSize * numChannels;
	float* frame = new float[totalFrameSize];
	short* buf   = new short[totalFrameSize];

	// Put bitstream header
	CChunkChunk* twinChunk = TvqCreateHeaderChunk( &setupInfo, "header_info" );
	if ( !twinChunk) {
		fprintf( stderr, "Failed to create bitstream header.\n" );
		return 1;
	}
	TvqPutBsHeaderInfo( bfp, *twinChunk );
	delete twinChunk;

	// Encode
	int nbuf, flushCount = 2;
	while( flushCount > 0 ) {
		// read audio data
		if ( (nbuf=fread(buf, sizeof(short), totalFrameSize, fp)) == 0 ) {
			--flushCount;
		}
		buftofr( nbuf, buf, frame, frameSize, numChannels );

		TvqEncodeFrame(frame, &index); // encode
		TvqWriteBsFrame(&index, bfp);  // put data into the bitstream file
	}
	
	// Terminate program
	TvqEncTerminate(&index);
	delete[] frame;
	delete[] buf;
	bclose(bfp);
	fclose(fp);
	
	return 0;
}

