/*
 * This is the Loris C++ Class Library, implementing analysis, 
 * manipulation, and synthesis of digitized sounds using the Reassigned 
 * Bandwidth-Enhanced Additive Sound Model.
 *
 * Loris is Copyright (c) 1999-2010 by Kelly Fitz and Lippold Haken
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY, without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * AiffData.h
 *
 * Declarations of import and export functions.
 *
 * Kelly Fitz, 17 Sept 2003 
 * loris@cerlsoundgroup.org
 *
 * http://www.cerlsoundgroup.org/Loris/
 *
 */

#include "Marker.h"

#include <string>
#include <vector>

//	in case configure wasn't run (no config.h), 
//	pick some (hopefully-) reasonable values for
//	these things and hope for the best...
#if ! defined( SIZEOF_SHORT )
#define SIZEOF_SHORT 2
#endif

#if ! defined( SIZEOF_INT )
#define SIZEOF_INT 4
#endif

#if ! defined( SIZEOF_LONG )
#define SIZEOF_LONG 4	// not for DEC Alpha!
#endif


#if SIZEOF_SHORT == 2
typedef short 			Int_16;
typedef unsigned short 	Uint_16;
#elif SIZEOF_INT == 2
typedef int 			Int_16;
typedef unsigned int 	Uint_16;
#else
#error "cannot find an appropriate type for 16-bit integers"
#endif

#if SIZEOF_INT == 4
typedef int 			Int_32;
typedef unsigned int 	Uint_32;
#elif SIZEOF_LONG == 4
typedef long 			Int_32;
typedef unsigned long 	Uint_32;
#else
#error "cannot find an appropriate type for 32-bit integers"
#endif


//	begin namespace
namespace Loris {

//	-- chunk types --
enum 
{ 
	ContainerId = 0x464f524d,				// 'FORM' 
	AiffType = 0x41494646,					// 'AIFF' 
	CommonId = 0x434f4d4d,					// 'COMM'
	ApplicationSpecificId = 0x4150504c,		// 'APPL'
	SosEnvelopesId = 0x534f5365,			// 'SOSe'
	SoundDataId = 0x53534e44,				// 'SSND'
	InstrumentId = 0x494e5354,				// 'INST'
	MarkerId = 0x4d41524b					// 'MARK'
};

typedef Uint_32 ID;
typedef char Byte;

struct CkHeader 
{
	ID id;
	Uint_32 size;
	
	//	providing this default constructor
	//	gives clients a way to determine whether
	//	a chunk has been read and assigned:
	CkHeader( void ) : id(0), size(0) {}
};

struct ContainerCk
{
	CkHeader header;
	ID formType;
};

struct CommonCk
{
	CkHeader header;
	Int_16 channels;			// number of channels 
	Int_32 sampleFrames;		// channel independent sample frames 
	Int_16 bitsPerSample;		// number of bits per sample 
	double srate;				// sampling rate (stored in IEEE 10 byte format)
};

struct SoundDataCk
{
	CkHeader header;	
	Uint_32 offset;				
	Uint_32 blockSize;	
	
	//	sample frames follow
	std::vector< Byte > sampleBytes;
};

struct MarkerCk 
{ 
	CkHeader header;	
	Uint_16  numMarkers; 
	
	struct Marker
	{ 
		Uint_16  markerID; 
		Uint_32	 position;	 		// position in uncompressed samples
		std::string markerName; 
	}; 
	
	std::vector< MarkerCk::Marker > markers;
}; 

struct InstrumentCk
{
	CkHeader header;	
	Byte	baseNote;		/* all notes are MIDI note numbers */ 
	Byte	detune;			/* cents off, only -50 to +50 are significant */ 
	Byte	lowNote; 
	Byte	highNote; 
	Byte	lowVelocity;	/* 1 to 127 */ 
	Byte	highVelocity;	/* 1 to 127 */ 
	Int_16	gain;			/* in dB, 0 is normal */ 

	struct Loop 
	{ 
		Int_16	playMode;	/* 0 - no loop, 1 - forward looping, 2 - backward looping */ 
		Uint_16 beginLoop;	/* this is a reference to a markerID, so you always 
	�������������������������������� have to work with MARK and INST together!! */ 
		Uint_16 endLoop; 
	}; 

	Loop sustainLoop;
	Loop releaseLoop; 
}; 


struct SosEnvelopesCk
{
	CkHeader header;	
	Int_32	signature;		// For SOS, should be 'SOSe'
	Int_32	enhanced;		// 0 for sine-only, 1 for bandwidth-enhanced
	Int_32	validPartials;	// Number of partials with data in them; the file must
							// be padded out to the next higher 2**n partials;
							// this number is doubled for enhanced files
	
	//	skip validPartials * sizeof(Int_32) bytes of junk here
	Int_32	resolution;		// frame duration in microseconds 
	Int_32	quasiHarmonic;	// how many of the partials are quasiharmonic
	//	skip 
	//		(4*LargestLabel + 8 - validPartials - 2) * sizeof(Int_32) 
	//	bytes of junk here	

/*				
	//	this stuff is unbelievably nasty!						
	
#define initPhaseLth ( 4*LargestLabel + 8 )
	Int_32	initPhase[initPhaseLth]; // obsolete initial phase array; is VARIABLE LENGTH array 
							// this is big enough for a max of 512 enhanced partials plus values below
//	Int_32	resolution;		// frame duration in microseconds 
	#define SOSresolution( es )   initPhase[ spcEI.enhanced \
											? 2 * spcEI.numPartials : spcEI.numPartials]	
							// follows the initPhase[] array
	
							
//	Int_32	quasiHarmonic;	// how many of the partials are quasiharmonic
	#define SOSquasiHarmonic( es )  initPhase[ spcEI.enhanced \
											? 2 * spcEI.numPartials + 1 : spcEI.numPartials + 1]	
							// follows the initPhase[] array
*/							
};

// ---------------------------------------------------------------------------
//	readChunkHeader
// ---------------------------------------------------------------------------
//	Read the id and chunk size from the current file position.
//	Let exceptions propogate.
//
std::istream &  
readChunkHeader( std::istream & s, CkHeader & h );


// ---------------------------------------------------------------------------
//	readApplicationSpecifcData
// ---------------------------------------------------------------------------
//	Read the data in the ApplicationSpecific chunk, assume the stream is 
//	correctly positioned, and that the chunk header has already been read.
//
//  Look for data specific to SPC files. Any other kind of Application 
//	Specific data is ignored.
//
std::istream & 
readApplicationSpecifcData( std::istream & s, SosEnvelopesCk & ck, unsigned long chunkSize );


// ---------------------------------------------------------------------------
//	readCommonData
// ---------------------------------------------------------------------------
//	Read the data in the Common chunk, assume the stream is correctly
//	positioned, and that the chunk header has already been read.
//
std::istream & 
readCommonData( std::istream & s, CommonCk & ck, unsigned long chunkSize );

// ---------------------------------------------------------------------------
//	readContainer
// ---------------------------------------------------------------------------
//
std::istream & 
readContainer( std::istream & s, ContainerCk & ck, unsigned long chunkSize );

// ---------------------------------------------------------------------------
//	readInstrumentData
// ---------------------------------------------------------------------------
//
std::istream & 
readInstrumentData( std::istream & s, InstrumentCk & ck, unsigned long chunkSize );

// ---------------------------------------------------------------------------
//	readMarkerData
// ---------------------------------------------------------------------------
//
std::istream & 
readMarkerData( std::istream & s, MarkerCk & ck, unsigned long chunkSize );

// ---------------------------------------------------------------------------
//	readSampleData
// ---------------------------------------------------------------------------
//	Read the data in the Sound Data chunk, assume the stream is correctly
//	positioned, and that the chunk header has already been read.
//
std::istream & 
readSampleData( std::istream & s, SoundDataCk & ck, unsigned long chunkSize );

// ---------------------------------------------------------------------------
//	configureCommonCk
// ---------------------------------------------------------------------------
void 
configureCommonCk( CommonCk & ck, unsigned long nFrames, unsigned int nChans,
				   unsigned int bps, double srate  );
				   
// ---------------------------------------------------------------------------
//	configureContainer
// ---------------------------------------------------------------------------
//	dataSize is the combined size of all other chunks in file. Configure 
//	them first, then add their sizes (with headers!).
//
void 
configureContainer( ContainerCk & ck, unsigned long dataSize );

// ---------------------------------------------------------------------------
//	configureInstrumentCk
// ---------------------------------------------------------------------------
void 
configureInstrumentCk( InstrumentCk & ck, double midiNoteNum );

// ---------------------------------------------------------------------------
//	configureMarkerCk
// ---------------------------------------------------------------------------
void 
configureMarkerCk( MarkerCk & ck, const std::vector< Marker > & markers, 
				   double srate  );

// ---------------------------------------------------------------------------
//	configureSoundDataCk
// ---------------------------------------------------------------------------
//
void 
configureSoundDataCk( SoundDataCk & ck, const std::vector< double > & samples, 
					  unsigned int bps  );

// ---------------------------------------------------------------------------
//	writeCommon
// ---------------------------------------------------------------------------
//
std::ostream &
writeCommonData( std::ostream & s, const CommonCk & ck );

// ---------------------------------------------------------------------------
//	writeContainer
// ---------------------------------------------------------------------------
//
std::ostream &
writeContainer( std::ostream & s, const ContainerCk & ck );

// ---------------------------------------------------------------------------
//	writeInstrumentData
// ---------------------------------------------------------------------------
std::ostream &
writeInstrumentData( std::ostream & s, const InstrumentCk & ck );

// ---------------------------------------------------------------------------
//	writeMarkerData
// ---------------------------------------------------------------------------
std::ostream &
writeMarkerData( std::ostream & s, const MarkerCk & ck );

// ---------------------------------------------------------------------------
//	writeSampleData
// ---------------------------------------------------------------------------
//
std::ostream & 
writeSampleData( std::ostream & s, const SoundDataCk & ck );

// ---------------------------------------------------------------------------
//	convertBytesToSamples
// ---------------------------------------------------------------------------
//	Convert sample bytes to double precision floating point samples 
//	(-1.0, 1.0). The samples vector is resized to fit exactly as many
//	samples as are represented in the bytes vector, and any prior
//	contents are overwritten.
//
void
convertBytesToSamples( const std::vector< Byte > & bytes, 
					   std::vector< double > & samples, unsigned int bps );

// ---------------------------------------------------------------------------
//	convertSamplesToBytes
// ---------------------------------------------------------------------------
//	Convert floating point samples (-1.0, 1.0) to bytes. 
//	The bytes vector is resized to fit exactly as many
//	samples as are stored in the samples vector, and any prior
//	contents are overwritten.
//
void
convertSamplesToBytes( const std::vector< double > & samples, 
					   std::vector< Byte > & bytes, unsigned int bps );
					   
} // end of namespace