Metropoli BBS
VIEWER: convert.c MODE: TEXT (ASCII)
/*
	CONVERT.C

	27-jun-88, Marc Savary, Ad Lib Inc.

	Module to convert *.ROL file to AdLib Midi file, *.MUS & *.SND

	The beginning of the .MUS file contains 70 bytes
	of start-up information (see convert.h file):
		1:      major version # of file
		1:      minor version # of file
		4:	melody identification
		30:	melody name
		1:      ticks per beat
		1:      beats per measure
		4:	length of melody, in ticks
		4:	length of melody's data part
		4:	total number of commands in melody, including NOTE-ON, NOTE-OFF,
			PROGRAM-CHANGE, AFTER-TOUCH, PITCH-BEND, OVERFLOW, SYSTEM-XOR
			& STOP
		8:	filler area, set to zero
		1:	sound mode: 0 == melodic, 1 == percussive
		1:  pitch bend range, from 1 to 12
		2:	basic tempo
		8:	filler area, set to zero

	Following the header is the musical data.
	The data part of the .MUS file contains midi 1.0 commands with timing
	bytes of 1/tickBeat. tickBeat is the number of divisions per beat.
	The relation between tickBeat, tempo (beat per minute) & frequency of
	the timing counter is:
		frequency = (tempo / 60) * tickBeat.
		
	Timing bytes vary from 0 to 0xFE and precede every command. A timing
	byte of 0xF8 means timing overflow with a value of 240. An overflow
	byte is always followed by another overflow byte or timing byte.

	Program changes refer to timbre definitions in the .SND bank file by their
	relative order. While converting files, if a timbre ( ref. by it's name)
	is not found in the bank file, its definition is appended to the end.

	A tempo change is specified within a SYSTEM EXCLUSIVE MESSAGE:
	0xF0 0x7F 00 <integer> <frac> 0xF7, where 'integer' is the integer
	part of the tempo multiplier and 'frac' the fractionnal part, in 1/128.

	Each voice is associated with one midi channel. In melodic mode,
	channels 0 to 8 are melodic; in percussive mode, channels 0 to 5
	are melodic and channels 6 to 11 are percussive.

	The last MIDI command of the file is STOP ( 0xFC).

	If compiling with Lattice, 'LATTICE' must be defined for MEMORY.C
	and BANK.C.

	With Microsoft, use the following ('MICROSOFT' must be defined for
	this file and BANK.C):
	  cl -DMICROSOFT -AS -J -Zi /Zp1 -Ox -Gs play.c bank.c adlib.c

*/

#include "mem.h"
#include "convert.h"
#include "adlib.h"
#include "bank.h"
#include "strmidi.h"

#include "fcntl.h"
#include "stdio.h"

#ifdef MICROSOFT
#include <sys\types.h>
#include <sys\stat.h>
#define  open(x,y)      open(x,y,S_IWRITE)
#define  setmem(x,y,z)  memset(x,z,y)
#define  min(x,y)       ((x < y) ? x:y)
#define  max(x,y)       ((x > y) ? x:y)
#endif

#define NR_MELO_VOICES 9		/* in melodic mode */
#define NR_PERC_VOICES 11		/* in percussive mode */

#define BLOCK_SIZE 512			/* must be power of two !!! */
#define instrumFileExtension ".INS"
#define musicFileExtension ".ROL"

#define MAJOR_VERSION	1
#define MINOR_VERSION	0

/* type of events, decreasing in order of priority: */
#define NOTE_OFF_TP	0
#define PRESET_TP		1
#define PITCH_TP		2
#define VOLUME_TP		3
#define TEMPO_TP		4
#define NOTE_ON_TP		5
#define NR_TYPES		6



typedef int Time;

typedef
	struct {
		unsigned nrBytes;
		unsigned size;
		Handle liste;
	} ListDesc;

typedef
	struct {
		Time	time;
		float	tempo;
	} TempoEvent;

typedef
	struct {
		int		note;
		int		duree;
	} NoteEvent;


typedef
	struct {
		Time time;
		char instrumName[ 9];
		char filler;
		int	timbreIndex;
	} InstrumEvent;

typedef
	struct {
		Time time;
		float volume;
	} VolumeEvent;


typedef
	struct {
		Time time;
		float pitch;
	} PitchEvent;

typedef
	struct {
		char instrumName[ 9];
		int	params[ 26];
	} TimbreDef;

typedef
	struct {
		long	rollTime;
		long	rollPos;
		char	voice;
		char	used;
		char	type;
	} Cedule;

#define NoteAtPos( vc, pos) ((NoteEvent *) *noteList[ vc].liste + pos)
#define PitchAtPos( vc, pos) ((PitchEvent *) *pitchList[ vc].liste + pos)
#define VolumeAtPos( vc, pos) ((VolumeEvent *) *volumeList[ vc].liste + pos)
#define InstrumAtPos( vc, pos) ((InstrumEvent *) *instrumList[ vc].liste + pos)
#define TempoAtPos( pos) ((TempoEvent *) *tempoList.liste + pos)

#define PitchSize( vc) ( pitchList[ vc].size)
#define InstrumSize( vc) ( instrumList[ vc].size)
#define VolumeSize( vc) ( volumeList[ vc].size)
#define TempoSize() ( tempoList.size)
#define NoteSize( vc) ( noteList[ vc].size)

ListDesc	instrumList[ NR_VOICES];
ListDesc	noteList[ NR_VOICES];
ListDesc	pitchList[ NR_VOICES];
ListDesc	volumeList[ NR_VOICES];
ListDesc	tempoList;
ListDesc	timbreList;
int		srcTickBeat,
			destTickBeat;
int		beatMeasure;
char		notPercusMode;
float		basicTempo;
int		pitchBRange;
Time        duree_piece;    /* length of piece */

Cedule		cedList[ NR_TYPES][ NR_VOICES];
long		offTime[ NR_VOICES];
long		mpuTime;
char		statusArray[ NR_TYPES] = { NOTE_ON_BYTE, PROG_CHANGE_BYTE,
									PITCH_BEND_BYTE, AFTER_TOUCH_BYTE,
									00, NOTE_ON_BYTE };
char		status;
long		byteCount;
long		commCount;
char		voiceVolume[ NR_VOICES];
int		outFile;
BankPtr 	timbBank;

extern long	ConvertTime();




main( argc, argv)
	int argc;
	char * argv[];
{
	char buff[ 150];

	if( argc < 4) {
		fprintf( stderr, "\nUSE: convert <infile.rol> <bankfile.tim> <outfile.mus>");
		exit( 1);
		}

	InitMemory( 32);
	ColdInit();

	timbBank = OpenBank( argv[ 2], 1);
	if( NULL == timbBank) {
		fprintf( stderr, "\nCannot open timbre bank file '%s'.", argv[ 2]);
		exit( 1);
		}

	outFile = open( argv[ 3], O_WRONLY + O_RAW + O_TRUNC + O_CREAT);
	if( -1 == outFile) {
		fprintf( stderr, "\nUnable to open write file '%s'.", argv[ 3]);
		exit( 1);
		}

	if( !ConvertFile( argv[ 1])) {
		fprintf( stderr, "\nUnable to open read file '%s'.", argv[ 1]);
		exit( 1);
		}
	close( outFile);
	CloseBank( timbBank);
}	/* main() */
		



ColdInit()
{
	int i;

	for( i = 0; i < NR_VOICES; i++) {
		instrumList[ i].size = 0;
		instrumList[ i].liste = NewHandle( (Size) 4);
		instrumList[ i].nrBytes = 4;

		noteList[ i].size = 0;
		noteList[ i].liste = NewHandle( (Size) 4);
		noteList[ i].nrBytes = 4;

		pitchList[ i].size = 0;
		pitchList[ i].liste = NewHandle( (Size) 4);
		pitchList[ i].nrBytes = 4;

		volumeList[ i].size = 0;
		volumeList[ i].liste = NewHandle( (Size) 4);
		volumeList[ i].nrBytes = 4;
		}
	tempoList.size = 0;
	tempoList.liste = NewHandle( (Size) 4);
	tempoList.nrBytes = 4;

	timbreList.size = 0;
	timbreList.liste = NewHandle( (Size) 4);
	timbreList.nrBytes = 4;

	/* Set the number of ticks per beat of the resulting conversion
	( must be a multiple of the ticks/beat of the .ROL file)
	This value is used for the conversion of the timing base. */
	destTickBeat = DEFAULT_TICK_BEAT;
}	/* ColdInit() */



/*
	For doing conversion, we first load the piece, and simulate
	the playing for ordering the events, using priority values when
	the events occur at the same time.
*/
int ConvertFile( fileName)
	char * fileName;
{

	int 	voix, nb_voices;
	Time	longueur;
	Cedule * ced;
	struct MusHeader mHd;
	extern Cedule * NextCedule();

	if( ! MyLoadMelo( fileName))
		return 0;
	LoadTimbres();

	setmem( &mHd, sizeof( struct MusHeader), 0);
	write( outFile, &mHd, sizeof( struct MusHeader));

	StartCedule();

	while( ced = NextCedule()) {
		switch( ced->type) {
			case PRESET_TP:	DoPreset( ced);	break;
			case PITCH_TP:	DoPitch( ced);	break;
			case VOLUME_TP:	DoVolume( ced);	break;
			case TEMPO_TP:	DoTempo( ced);	break;
			case NOTE_ON_TP:	DoNoteOn( ced);	break;
			case NOTE_OFF_TP:	DoNoteOff( ced);	break;
			default:		printf( "\nBad event type: %d", ced->type);
			}
		}
	DoEndMelo();

	mHd.majorVersion = MAJOR_VERSION;
	mHd.minorVersion = MINOR_VERSION;
	mHd.tickBeat = destTickBeat;
	mHd.beatMeasure = beatMeasure;
	mHd.totalTick = ConvertTime( duree_piece);
	mHd.dataSize = byteCount;
	mHd.nrCommand = commCount;
	mHd.soundMode = !notPercusMode;
	mHd.pitchBRange = pitchBRange;
	mHd.basicTempo = basicTempo;

	/* write header to output file: */
	lseek( outFile, 0L, 0);
	write( outFile, &mHd, sizeof( struct MusHeader));
}	/* ConvertFile() */



/*
	Start the simulation of playing a melody.
*/
StartCedule()
	{
	int i, j, k;
	Cedule * ced;

	ced = &cedList[ 0][ 0];
	for( i = 0; i < NR_TYPES; i++)
		for( k = 0; k < NR_VOICES; k++, ced++) {
			ced->rollTime = 0;
			ced->rollPos = 0;
			ced->used = 0;
			ced->voice = k;
			ced->type = i;
			}
	for( k = 0; k < NR_VOICES; k++) {
		CedulePreset( k, 0);
		CedulePitch( k, 0);
		CeduleVolume( k, 0);
		offTime[ k] = 0;
		CeduleNoteOn( k, 0);
		}
	CeduleTempo( 0);
	mpuTime = 0;
	status = 0;
	byteCount = 0;
	commCount = 0;
	}	/* StartCedule() */


/*
	Find and return the next event with the lowest time value.
	Return NULL if no more.
*/
Cedule * NextCedule()
	{
	Cedule * ced, * next;
	long lowTime;
	int type, vc;

	ced = &cedList[ 0][ 0];
	next = NULL;
	lowTime = 0x7fffffff;
	for( type = 0; type < NR_TYPES; type++)
		for( vc = 0; vc < NR_VOICES; vc++, ced++) {
			if( ! ced->used)
				continue;
			if( ced->rollTime < lowTime) {
				lowTime = ced->rollTime;
				next = ced;
				}
			}
	return next;
	}	/* NextCedule() */


/*
	Insert in the schedule list the note starting at position
	'pos' of the .ROL note list of voice 'voice'
*/
CeduleNoteOn( voice, pos)
	{
	unsigned size;
	Cedule * ced;
	ListDesc * lD;
	NoteEvent * nE;

	ced = &cedList[ NOTE_ON_TP][ voice];
	lD = &noteList[ voice];
	size = lD->size;
	ced->used = 0;
	nE = (NoteEvent *) *lD->liste + pos;
	while( nE->note == 0 && pos < size) {
		/* silences are notes of pitch == 0, and must be skiped */
		offTime[ voice] += nE->duree;
		nE++;
		pos++;
		}
	if( pos < size) {
		ced->rollPos = pos;
		ced->rollTime = offTime[ voice];
		ced->used = 1;
		offTime[ voice] += nE->duree;
		}
	}	/* CeduleNoteOn() */


CeduleNoteOff( voice, pos)
	{
	Cedule * ced;

	ced = &cedList[ NOTE_OFF_TP][ voice];
	ced->rollTime = offTime[ voice];
	ced->rollPos = pos;
	ced->used = 1;
	}	/* CeduleNoteOff() */



CedulePreset( voice, pos)
	{
	Cedule * ced;
	InstrumEvent * iE;

	if( NoteSize( voice) == 0)
		return;
	ced = &cedList[ PRESET_TP][ voice];
	if( pos >= InstrumSize( voice))
		ced->used = 0;
	else {
		iE = InstrumAtPos( voice, pos);
		ced->rollTime = iE->time;
		ced->rollPos = pos;
		ced->used = 1;
		}
	}	/* CedulePreset() */


CedulePitch( voice, pos)
	{
	Cedule * ced;
	PitchEvent * pE;

	if( NoteSize( voice) == 0)
		return;
	ced = &cedList[ PITCH_TP][ voice];
	if( pos >= PitchSize( voice))
		ced->used = 0;
	else {
		pE = PitchAtPos( voice, pos);
		ced->rollPos = pos;
		ced->rollTime = pE->time;
		ced->used = 1;
		}
	}	/* CedulePitch() */


CeduleVolume( voice, pos)
	{
	Cedule * ced;
	VolumeEvent * vE;

	if( NoteSize( voice) == 0)
		return;
	ced = &cedList[ VOLUME_TP][ voice];
	if( pos >= VolumeSize( voice))
		ced->used = 0;
	else {
		vE = VolumeAtPos( voice, pos);
		ced->rollPos = pos;
		ced->rollTime = vE->time;
		ced->used = 1;
		}
	}	/* CeduleVolume() */


CeduleTempo( pos)
	{
	Cedule * ced;
	TempoEvent * tE;

	ced = &cedList[ TEMPO_TP][ 0];
	if( pos >= TempoSize())
		ced->used = 0;
	else {
		tE = TempoAtPos( pos);
		ced->rollPos = pos;
		ced->rollTime = tE->time;
		ced->used = 1;
		}
	}	/* CeduleTempo() */




/*
	In the normal sequencing order, a note-on must be done
	on the voice & note specified by 'ced'.

	Note-on command: <timing> <9n> <pitch> <volume>
	where 'n' is the voice number.
*/
DoNoteOn( ced)
	Cedule * ced;
	{
	NoteEvent * nE;
	unsigned vol;

	DoTiming( ced);
	DoStatus( ced);
	nE = NoteAtPos( ced->voice, ced->rollPos);
	DoData( nE->note);
	vol = max( 1, voiceVolume[ ced->voice]);
	DoData( vol);

	/* schedule the note-off of this note: */
	CeduleNoteOff( ced->voice, ced->rollPos);

	/* schedule the next note-on for this voice: */
	CeduleNoteOn( ced->voice, ced->rollPos +1);
	}	/* DoNoteOn() */


/*
	Note-off command: <timing> <9n> <pitch> <00>
	where 'n' is the voice number.
*/
DoNoteOff( ced)
	Cedule * ced;
	{
	NoteEvent * nE;

	DoTiming( ced);
	DoStatus( ced);
	nE = NoteAtPos( ced->voice, ced->rollPos);
	DoData( nE->note);
	DoData( 0);
	ced->used = 0;
	}	/* DoNoteOff() */


/*
	Pitch command: <timing> <En> <pitch-low> <pitch-high>
	where 'n' is the voice number.
*/
DoPitch( ced)
	Cedule * ced;
	{
	PitchEvent * pE;
	unsigned uPitch;

	DoTiming( ced);
	DoStatus( ced);
	pE = PitchAtPos( ced->voice, ced->rollPos);
	uPitch = pE->pitch * MID_PITCH;
	uPitch = min( MAX_PITCH, uPitch);
	DoData( uPitch);
	DoData( uPitch >> 7);
	CedulePitch( ced->voice, ced->rollPos +1);
	}	/* DoPitch() */


/*
	After touch command: <timing> <An> <volume>
	where 'n' is the voice number.
*/
DoVolume( ced)
	Cedule * ced;
	{
	VolumeEvent * vE;
	NoteEvent * nE;
	Cedule * nCed;
	unsigned volume;

	vE = VolumeAtPos( ced->voice, ced->rollPos);
	volume = vE->volume * MAX_VOLUME;
	nCed = &cedList[ NOTE_ON_TP][ ced->voice];
	if( nCed->used && nCed->rollTime == ced->rollTime) {
		/* velocity (volume) is occuring in the same time than a note-on
			on this voice; it will be sent with the note-on itself. ... */
		;
		}
	else {
		DoTiming( ced);
		DoStatus( ced);
		DoData( volume);
		}
	voiceVolume[ ced->voice] = volume;
	CeduleVolume( ced->voice, ced->rollPos +1);
	}	/* DoVolume() */


/*
	Preset change command: <timing> <Cn> <preset>
	where 'n' is the voice number.
*/
DoPreset( ced)
	Cedule * ced;
	{
	InstrumEvent * iE;

	DoTiming( ced);
	DoStatus( ced);
	iE = InstrumAtPos( ced->voice, ced->rollPos);
	DoData( iE->timbreIndex);
	CedulePreset( ced->voice, ced->rollPos +1);
	}	/* DoPreset() */


/*
	Tempo:  <timing> <F0> <7F> <00> <integer> <frac> <F7>
	(non standard)
*/
DoTempo( ced)
	Cedule * ced;
	{
	TempoEvent * tE;
	unsigned ent, frac;

	tE = TempoAtPos( ced->rollPos);
	ent = min( (unsigned)tE->tempo, 127);
	frac = (tE->tempo - ent) * 128;
	DoTiming( ced);
	PutByte( SYSTEM_XOR_BYTE);
	status = 0;                     /* status must be re-sent next time */
	PutByte( ADLIB_CTRL_BYTE);
	PutByte( TEMPO_CTRL_BYTE);
	DoData( ent);
	DoData( frac);
	PutByte( EOX_BYTE);
	CeduleTempo( ced->rollPos +1);
	}	/* DoTempo() */


/*
	End melody: <timing> <FC>
*/
DoEndMelo()
	{
	commCount++;
	PutByte( 0);
	PutByte( STOP_BYTE);
	}	/* DoEndMelo() */



/*
	Add the timing bytes. If the delay ( in "n/destTickBeat" Beats)
	is greater than or equal to OVERFLOW, insert enough overflow bytes
	before the timing byte.
*/
DoTiming( ced)
	Cedule * ced;
	{
	long	time;

	commCount++;
	time = ConvertTime( ced->rollTime);
	while( time - OVERFLOW >= mpuTime) {
		commCount++;
		PutByte( OVERFLOW_BYTE);
		mpuTime += OVERFLOW;
		}
	PutByte( time - mpuTime);
	mpuTime = time;
	}	/* DoTiming() */


/*
	Convert the time 'rollTime' from the base 'srcTickBeat' (.ROL) to
	the base 'destTickBeat' ( .MUS)
*/
long ConvertTime( rollTime)
	Time rollTime;
	{
	long time;

	time = (long)rollTime * destTickBeat;
	time /= srcTickBeat;
	return time;
	}	/* ConverTime() */


/*
	Generate a status byte for the NOTE-ON, NOTE-OFF, PRESET-CHANGE, PITCH-BEND
	or AFTER-TOUCH (volume) midi command.
*/
DoStatus( ced)
	Cedule * ced;
	{
	char newStatus;

	newStatus = statusArray[ ced->type] + ced->voice;
	if( newStatus != status) {
		status = newStatus;
		PutByte( status);
		}
	}	/* DoStatus() */


/*
	Send data byte (high bit set to 0) to output file.
*/
DoData( data)
	char data;
	{
	PutByte( data & 0x7f);
	}	/* DoData() */


/*
	Send byte to output file, increment byte counter.
*/
PutByte( data)
	char data;
	{
	write( outFile, &data, 1);
	byteCount++;
	}	/* PutByte() */





/*
-------------------------------------------------------------
	The following routines are used to load a .ROL file in memory.
	Allocate memory using the routines in the MEMORY.C file.
-------------------------------------------------------------
*/


SizeList( liste, nrElems, sizeElem)
	ListDesc * liste;
	unsigned nrElems;
	unsigned sizeElem;
{
	unsigned newSize;

	newSize = nrElems * sizeElem;
	if( newSize > liste->nrBytes) {
		newSize = ( newSize + BLOCK_SIZE) & ( ~ (BLOCK_SIZE -1));
		SetHandleSize( liste->liste, (Size)newSize);
		if( MemError())
			SysErr( MemError(), 0);
		liste->nrBytes = newSize;
		}
	liste->size = nrElems;
}	/* SizeListe() */


SysErr( err, type)
	{
	fprintf( stderr, "\nMemory allocation error, code= %d", err);
	exit( 1);
	}


	
SizeTempo( size)
{
	SizeList( &tempoList, size, sizeof( TempoEvent));
}	/* SizeTempo() */

SizePitch( voice, size)
{
	SizeList( &pitchList[ voice], size, sizeof( PitchEvent));
}	/* SizePitch() */

SizeInstrum( voice, size)
{
	SizeList( &instrumList[ voice], size, sizeof( InstrumEvent));
}	/* SizeInstrum() */

SizeVolume( voice, size)
{
	SizeList( &volumeList[ voice], size, sizeof( VolumeEvent));
}	/* SizeVolume() */

SizeNote( voice, size)
{
	SizeList( &noteList[ voice], size, sizeof( NoteEvent));
}	/* SizeNote() */

SizeTimbre( size)
{
	SizeList( &timbreList, size, sizeof( TimbreDef));
}	/* SizeTimbre() */



TempoEvent * TempoPtr( pos)
{
	return ( TempoEvent *) *tempoList.liste + pos;
}	/* TempoPtr() */

VolumeEvent * VolumePtr( voice, pos)
{
	return ( VolumeEvent *) *volumeList[ voice].liste + pos;
}	/* VolumePtr() */

PitchEvent * PitchPtr( voice, pos)
{
	return ( PitchEvent *) *pitchList[ voice].liste + pos;
}	/* PitchPtr() */

InstrumEvent * InstrumPtr( voice, pos)
{
	return ( InstrumEvent *) *instrumList[ voice].liste + pos;
}	/* InstrumPtr() */

NoteEvent  * NotePtr( voice, pos)
{
	return ( NoteEvent *) *noteList[ voice].liste + pos;
}	/* NotePtr() */

TimbreDef * TimbrePtr( pos)
{
	return ( TimbreDef *) *timbreList.liste + pos;
}	/* TimbrePtr() */




int MyLoadMelo( fileName)
	char * fileName;
{
	int fileId, i;
	char buffer[ 255];

	pitchBRange = 1;

	strcpy( buffer, fileName);
/*	strcat( buffer, musicFileExtension);	*/
	fileId = open( buffer, O_RDONLY + O_RAW);
	if( fileId == -1)
		return 0;

	duree_piece = 0;

	if( 4 != read( fileId, buffer, 4))	/* skip file version, major & minor */
		return 0;
	if( 40 != read( fileId, buffer, 40))	/* skip filler field */
		return 0;
	if( 2 != read( fileId, &srcTickBeat, 2))
		return 0;
	if( 2 != read( fileId, &beatMeasure, 2))
		return 0;
	if( 5 != read( fileId, buffer, 5))	/* skip Y scale, X scale & filer */
		return 0;
	if( 1 != read( fileId, &notPercusMode, 1))
		return 0;
	if( (90 + 38 + 15) != read( fileId, buffer, 90 + 38 + 15))
		return 0;					/* skip 3 filler field */
	if( 4 != read( fileId, &basicTempo, 4))
		return 0;
	if( ! LoadTempo( fileId))
		return 0;

	for( i = 0; i < NR_VOICES; i++) {
		if( 15 != read( fileId, buffer, 15))	/* skip filler */
			return 0;
		if( ! LoadNotes( fileId, i))
			return 0;
		if( 15 != read( fileId, buffer, 15))
			return 0;
		if( ! LoadInstrumEvt( fileId, i))
			return 0;
		if( 15 != read( fileId, buffer, 15))
			return 0;
		if( ! LoadVolume( fileId, i))
			return 0;
		if( 15 != read( fileId, buffer, 15))
			return 0;
		if( ! LoadPitch( fileId, i))
			return 0;
		}
	LisMidiData( fileId);
	close( fileId);
	return 1;
}	/* MyLoadMelo() */



int LoadPitch( fileId, voice)
{
	int size, i, ok;
	PitchEvent * pEvent;

	if( 2 != read( fileId, &size, 2))
		return 0;
	SizePitch( voice, size);
	pEvent = PitchPtr( voice, 0);
	ok = 1;
	for( i = 0; i < size && ok; i++, pEvent++)
		ok = ( 6 == read( fileId, pEvent, 6));
	return ok;
}	/* LoadPitch() */


int LoadVolume( fileId, voice)
{
	int size, i, ok;
	VolumeEvent * vEvent;

	if( 2 != read( fileId, &size, 2))
		return 0;
	SizeVolume( voice, size);
	vEvent = VolumePtr( voice, 0);
	ok = 1;
	for( i = 0; i < size && ok; i++, vEvent++) {
		ok = ( 6 == read( fileId, vEvent, 6));
		}
	return ok;
}	/* LoadVolume() */
	



int LoadInstrumEvt( fileId, voice)
{
	int size, ok, i;
	InstrumEvent * iEvent;

	if( 2 != read( fileId, &size, 2))
		return 0;
	ok = 1;
	SizeInstrum( voice, size);
	iEvent = InstrumPtr( voice, 0);
	for( i = 0; i < size && ok; i++, iEvent++)
		ok = ( 14 == read( fileId, iEvent, 14));
	return ok;
}	/* LoadInstrumEvt() */

				


int LoadNotes( fileId, voice)
{
	NoteEvent * nEvent;
	Time time, lastTime;
	int count, ok;

	time = 0;
	if( 2 != read( fileId, &lastTime, 2))
		return 0;
	ok = 1;
	count = 0;
	SizeNote( voice, count);
	while( time < lastTime && ok ) {
		SizeNote( voice, count + 1);
		nEvent = NotePtr( voice, count);
		count++;
		ok = ( 4 == read( fileId, nEvent, 4));
		time += nEvent->duree;
		}
	duree_piece = max( duree_piece, lastTime);
	return ok;
}	/* LoadNotes() */


	



int LoadTempo( fileId)
{
	int size, i, ok;
	TempoEvent * tEvent;

	if( 2 != read( fileId, &size, 2))
		return 0;
	
	SizeTempo( size);
	tEvent = TempoPtr( 0);
	ok = 1;
	for( i = 0; i < size && ok; i++, tEvent++) {
		ok = ( 6 == read( fileId, tEvent, 6));
		}
	return ok;
}	/* LoadTempo() */

	

/*
	If the .ROL file was written by the "Visual Composer/MIDI Supplement",
	the pitch bend range is read from the file.
*/
LisMidiData( fileId)
	{
	struct midi_vars midiParam;
	int i;
	
	midiParam.midiVersion_ = 0;
	read( fileId, &midiParam, sizeof( struct midi_vars));
	if( midiParam.midiVersion_) {
		/* le fichier contient le data MIDI ... */
		pitchBRange = midiParam.pitchRange_;
		}
	}	/* LisMidiData() */


/*
	For each timbre reference in the .ROL file, search the index
	of the bank file (.SND) for the timbre.  If it exists, get the relative
	position of the definition, if not, search in the current directory for
	the timbre, and add it to the end of the bank file. The relative position
	is the value saved with the command PROGRAM-CHANGE in the .MUS file.

	If the timbre is not found, a warning message is printed on the screen,
	and the timbre reference value is set to 0 ( first timbre of bank).
*/
LoadTimbres()
{
	int voice, j, k, ok, nrDefs, fileId, timbPos;
	int timbreDef[ TIMBRE_DEF_LEN];
	InstrumEvent * iEvent;
	char fileN[ 80];
	char buff[ 10];

	for( voice = 0; voice < NR_VOICES; voice++) {
		iEvent = InstrumPtr( voice, 0);
		for( j = instrumList[ voice].size; j; j--, iEvent++) {
			iEvent->timbreIndex = 0;	/* the first, by default */

			if( GetTimbre( iEvent->instrumName, &timbPos, timbreDef, timbBank))
				iEvent->timbreIndex = timbPos;
			else {
				setmem( timbreDef, TIMBRE_DEF_SIZE, 0);
				strcpy( fileN, iEvent->instrumName);	
				strcat( fileN, instrumFileExtension);
				fileId = open( fileN, O_RDONLY + O_RAW);
				if( fileId != -1) {
					read( fileId, buff, 2);		/* skip instrum header */
					read( fileId, timbreDef, TIMBRE_DEF_SIZE);
					close( fileId);
					AddTimbre( iEvent->instrumName, &timbPos, timbreDef, timbBank);
					iEvent->timbreIndex = timbPos;
					}
				else {
					fprintf( stderr, "\nInstrument not found: %s", fileN);
					}
				}
			}
		}
}	/* LoadTimbres() */







[ RETURN TO DIRECTORY ]