Beispiel #1
0
InstrumentSetup *_af_instsetup_new (int instrumentCount)
{
	InstrumentSetup	*instruments;

	if (instrumentCount == 0)
		return NULL;
	instruments = (InstrumentSetup *) _af_calloc(instrumentCount, sizeof (InstrumentSetup));
	if (instruments == NULL)
		return NULL;

	for (int i=0; i<instrumentCount; i++)
	{
		instruments[i] = _af_default_instrumentsetup;
		instruments[i].id = AF_DEFAULT_INST + i;
		if (instruments[i].loopCount == 0)
			instruments[i].loops = NULL;
		else
		{
			instruments[i].loops = (LoopSetup *) _af_calloc(instruments[i].loopCount, sizeof (LoopSetup));
			if (instruments[i].loops == NULL)
				return NULL;

			for (int j=0; j<instruments[i].loopCount; j++)
				instruments[i].loops[j].id = j+1;
		}
	}

	return instruments;
}
Beispiel #2
0
TrackSetup *_af_tracksetup_new (int trackCount)
{
	TrackSetup	*tracks;

	if (trackCount == 0)
		return NULL;

	tracks = (TrackSetup *) _af_calloc(trackCount, sizeof (TrackSetup));
	if (tracks == NULL)
		return NULL;

	for (int i=0; i<trackCount; i++)
	{
		tracks[i] = _af_default_tracksetup;

		tracks[i].id = AF_DEFAULT_TRACK + i;

		/* XXXmpruett deal with compression */

		_af_set_sample_format(&tracks[i].f, tracks[i].f.sampleFormat,
			tracks[i].f.sampleWidth);

		if (tracks[i].markerCount == 0)
			tracks[i].markers = NULL;
		else
		{
			tracks[i].markers = (MarkerSetup *) _af_calloc(tracks[i].markerCount,
				sizeof (MarkerSetup));

			if (tracks[i].markers == NULL)
				return NULL;

			for (int j=0; j<tracks[i].markerCount; j++)
			{
				tracks[i].markers[j].id = j+1;

				tracks[i].markers[j].name = _af_strdup("");
				if (tracks[i].markers[j].name == NULL)
					return NULL;

				tracks[i].markers[j].comment = _af_strdup("");
				if (tracks[i].markers[j].comment == NULL)
					return NULL;
			}
		}
	}

	return tracks;
}
Beispiel #3
0
void afInitMarkIDs(AFfilesetup setup, int trackid, const int *markids, int nmarks)
{
	if (!_af_filesetup_ok(setup))
		return;

	TrackSetup *track = setup->getTrack(trackid);
	if (!track)
		return;

	if (track->markers != NULL)
	{
		for (int i=0; i<track->markerCount; i++)
		{
			if (track->markers[i].name != NULL)
				free(track->markers[i].name);
			if (track->markers[i].comment != NULL)
				free(track->markers[i].comment);
		}
		free(track->markers);
	}

	track->markers = (MarkerSetup *) _af_calloc(nmarks, sizeof (struct MarkerSetup));
	track->markerCount = nmarks;

	for (int i=0; i<nmarks; i++)
	{
		track->markers[i].id = markids[i];
		track->markers[i].name = _af_strdup("");
		track->markers[i].comment = _af_strdup("");
	}

	track->markersSet = true;
}
Beispiel #4
0
AFfilesetup afNewFileSetup (void)
{
	AFfilesetup	setup;

	setup = (_AFfilesetup *) _af_malloc(sizeof (_AFfilesetup));
	if (setup == NULL) return AF_NULL_FILESETUP;

	*setup = _af_default_file_setup;

	setup->tracks = _af_tracksetup_new(setup->trackCount);

	setup->instruments = _af_instsetup_new(setup->instrumentCount);

	if (setup->miscellaneousCount == 0)
		setup->miscellaneous = NULL;
	else
	{
		setup->miscellaneous = (MiscellaneousSetup *) _af_calloc(setup->miscellaneousCount,
			sizeof (MiscellaneousSetup));
		for (int i=0; i<setup->miscellaneousCount; i++)
		{
			setup->miscellaneous[i].id = i+1;
			setup->miscellaneous[i].type = 0;
			setup->miscellaneous[i].size = 0;
		}
	}

	return setup;
}
Beispiel #5
0
void afInitLoopIDs (AFfilesetup setup, int instid, int *loopids, int nloops)
{
	int instno;

	if (!_af_filesetup_ok(setup))
		return;

	if (!_af_unique_ids(loopids, nloops, "loop", AF_BAD_LOOPID))
		return;

	if ((instno = _af_setup_instrument_index_from_id(setup, instid)) == -1)
		return;

	_af_setup_free_loops(setup, instno);

	setup->instruments[instno].loopCount = nloops;
	setup->instruments[instno].loopSet = true;

	if (nloops == 0)
		setup->instruments[instno].loops = NULL;
	else
	{
		int i;

		if ((setup->instruments[instno].loops = _af_calloc(nloops, sizeof (_LoopSetup))) == NULL)
			return;

		for (i=0; i < nloops; i++)
			setup->instruments[instno].loops[i].id = loopids[i];
	}
}
bool InstrumentSetup::allocateLoops(int count)
{
	freeLoops();
	loops = (LoopSetup *) _af_calloc(count, sizeof (LoopSetup));
	if (loops)
	{
		loopCount = count;
		return true;
	}
	return false;
}
Beispiel #7
0
status WAVEFile::writeCues()
{
	int *markids, markCount;
	uint32_t numCues, cueChunkSize, listChunkSize;

	markCount = afGetMarkIDs(this, AF_DEFAULT_TRACK, NULL);
	if (markCount == 0)
		return AF_SUCCEED;

	if (markOffset == 0)
		markOffset = fh->tell();
	else
		fh->seek(markOffset, File::SeekFromBeginning);

	fh->write("cue ", 4);

	/*
		The cue chunk consists of 4 bytes for the number of cue points
		followed by 24 bytes for each cue point record.
	*/
	cueChunkSize = 4 + markCount * 24;
	writeU32(&cueChunkSize);
	numCues = markCount;
	writeU32(&numCues);

	markids = (int *) _af_calloc(markCount, sizeof (int));
	assert(markids != NULL);
	afGetMarkIDs(this, AF_DEFAULT_TRACK, markids);

	/* Write each marker to the file. */
	for (int i=0; i < markCount; i++)
	{
		uint32_t	identifier, position, chunkStart, blockStart;
		uint32_t	sampleOffset;
		AFframecount	markposition;

		identifier = markids[i];
		writeU32(&identifier);

		position = i;
		writeU32(&position);

		/* For now the RIFF id is always the first data chunk. */
		fh->write("data", 4);

		/*
			For an uncompressed WAVE file which contains
			only one data chunk, chunkStart and blockStart
			are zero.
		*/
		chunkStart = 0;
		fh->write(&chunkStart, sizeof (uint32_t));

		blockStart = 0;
		fh->write(&blockStart, sizeof (uint32_t));

		markposition = afGetMarkPosition(this, AF_DEFAULT_TRACK, markids[i]);

		/* Sample offsets are stored in the WAVE file as frames. */
		sampleOffset = markposition;
		writeU32(&sampleOffset);
	}

	/*
		Now write the cue names which is in a master list chunk
		with a subchunk for each cue's name.
	*/

	listChunkSize = 4;
	for (int i=0; i<markCount; i++)
	{
		const char *name;

		name = afGetMarkName(this, AF_DEFAULT_TRACK, markids[i]);

		/*
			Each label chunk consists of 4 bytes for the
			"labl" chunk ID, 4 bytes for the chunk data
			size, 4 bytes for the cue point ID, and then
			the length of the label as a Pascal-style string.

			In all, this is 12 bytes plus the length of the
			string, its size byte, and a trailing pad byte
			if the length of the chunk is otherwise odd.
		*/
		listChunkSize += 12 + (strlen(name) + 1) +
			((strlen(name) + 1) % 2);
	}

	fh->write("LIST", 4);
	writeU32(&listChunkSize);
	fh->write("adtl", 4);

	for (int i=0; i<markCount; i++)
	{
		const char	*name;
		uint32_t	labelSize, cuePointID;

		name = afGetMarkName(this, AF_DEFAULT_TRACK, markids[i]);

		/* Make labelSize even if it is not already. */
		labelSize = 4+(strlen(name)+1) + ((strlen(name) + 1) % 2);
		cuePointID = markids[i];

		fh->write("labl", 4);
		writeU32(&labelSize);
		writeU32(&cuePointID);
		fh->write(name, strlen(name) + 1);
		/*
			If the name plus the size byte comprises an odd
			length, add another byte to make the string an
			even length.
		*/
		if (((strlen(name) + 1) % 2) != 0)
		{
			uint8_t	zero=0;
			writeU8(&zero);
		}
	}

	free(markids);

	return AF_SUCCEED;
}
Beispiel #8
0
static status WriteCues (AFfilehandle file)
{
	int		i, *markids, markCount;
	uint32_t	numCues, cueChunkSize, listChunkSize;
	_WAVEInfo	*wave;

	assert(file);

	markCount = afGetMarkIDs(file, AF_DEFAULT_TRACK, NULL);
	if (markCount == 0)
		return AF_SUCCEED;

	wave = file->formatSpecific;

	if (wave->markOffset == 0)
		wave->markOffset = af_ftell(file->fh);
	else
		af_fseek(file->fh, wave->markOffset, SEEK_SET);

	af_fwrite("cue ", 4, 1, file->fh);

	/*
		The cue chunk consists of 4 bytes for the number of cue points
		followed by 24 bytes for each cue point record.
	*/
	cueChunkSize = 4 + markCount * 24;
	af_write_uint32_le(&cueChunkSize, file->fh);
	numCues = markCount;
	af_write_uint32_le(&numCues, file->fh);

	markids = _af_calloc(markCount, sizeof (int));
	assert(markids != NULL);
	afGetMarkIDs(file, AF_DEFAULT_TRACK, markids);

	/* Write each marker to the file. */
	for (i=0; i < markCount; i++)
	{
		uint32_t	identifier, position, chunkStart, blockStart;
		uint32_t	sampleOffset;
		AFframecount	markposition;

		identifier = markids[i];
		af_write_uint32_le(&identifier, file->fh);

		position = i;
		af_write_uint32_le(&position, file->fh);

		/* For now the RIFF id is always the first data chunk. */
		af_fwrite("data", 4, 1, file->fh);

		/*
			For an uncompressed WAVE file which contains
			only one data chunk, chunkStart and blockStart
			are zero.
		*/
		chunkStart = 0;
		af_fwrite(&chunkStart, sizeof (uint32_t), 1, file->fh);

		blockStart = 0;
		af_fwrite(&blockStart, sizeof (uint32_t), 1, file->fh);

		markposition = afGetMarkPosition(file, AF_DEFAULT_TRACK, markids[i]);

		/* Sample offsets are stored in the WAVE file as frames. */
		sampleOffset = markposition;
		af_write_uint32_le(&sampleOffset, file->fh);
	}

	/*
		Now write the cue names which is in a master list chunk
		with a subchunk for each cue's name.
	*/

	listChunkSize = 4;
	for (i=0; i<markCount; i++)
	{
		const char *name;

		name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]);

		/*
			Each label chunk consists of 4 bytes for the
			"labl" chunk ID, 4 bytes for the chunk data
			size, 4 bytes for the cue point ID, and then
			the length of the label as a Pascal-style string.

			In all, this is 12 bytes plus the length of the
			string, its size byte, and a trailing pad byte
			if the length of the chunk is otherwise odd.
		*/
		listChunkSize += 12 + (strlen(name) + 1) +
			((strlen(name) + 1) % 2);
	}

	af_fwrite("LIST", 4, 1, file->fh);
	af_write_uint32_le(&listChunkSize, file->fh);
	af_fwrite("adtl", 4, 1, file->fh);

	for (i=0; i<markCount; i++)
	{
		const char	*name;
		uint32_t	labelSize, cuePointID;

		name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]);

		/* Make labelSize even if it is not already. */
		labelSize = 4+(strlen(name)+1) + ((strlen(name) + 1) % 2);
		cuePointID = markids[i];

		af_fwrite("labl", 4, 1, file->fh);
		af_write_uint32_le(&labelSize, file->fh);
		af_write_uint32_le(&cuePointID, file->fh);
		af_fwrite(name, strlen(name) + 1, 1, file->fh);
		/*
			If the name plus the size byte comprises an odd
			length, add another byte to make the string an
			even length.
		*/
		if (((strlen(name) + 1) % 2) != 0)
		{
			uint8_t	zero=0;
			af_write_uint8(&zero, file->fh);
		}
	}

	free(markids);

	return AF_SUCCEED;
}
Beispiel #9
0
/* ARGSUSED3 */
AUpvlist _afQueryFileFormat (int arg1, int arg2, int arg3, int arg4)
{
	switch (arg1)
	{
		/* The following select only on arg1. */
		case AF_QUERY_ID_COUNT:
		{
			int	count = 0, idx;
			for (idx = 0; idx < _AF_NUM_UNITS; idx++)
				if (_af_units[idx].implemented)
					count++;
			return _af_pv_long(count);
		}
		/* NOTREACHED */
		break;

		case AF_QUERY_IDS:
		{
			int	count = 0, idx;
			int	*buffer;

			buffer = (int *) _af_calloc(_AF_NUM_UNITS, sizeof (int));
			if (buffer == NULL)
				return AU_NULL_PVLIST;

			for (idx = 0; idx < _AF_NUM_UNITS; idx++)
				if (_af_units[idx].implemented)
					buffer[count++] = idx;

			if (count == 0)
			{
				free(buffer);
				return AU_NULL_PVLIST;
			}

			return _af_pv_pointer(buffer);
		}
		/* NOTREACHED */
		break;

		/* The following select on arg2. */
		case AF_QUERY_LABEL:
			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			return _af_pv_pointer(const_cast<char *>(_af_units[arg2].label));

		case AF_QUERY_NAME:
			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			return _af_pv_pointer(const_cast<char *>(_af_units[arg2].name));

		case AF_QUERY_DESC:
			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			return _af_pv_pointer(const_cast<char *>(_af_units[arg2].description));

		case AF_QUERY_IMPLEMENTED:
			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return _af_pv_long(0);
			return _af_pv_long(_af_units[arg2].implemented);

		/* The following select on arg3. */
		case AF_QUERY_SAMPLE_FORMATS:
			if (arg3 < 0 || arg3 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			switch (arg2)
			{
				case AF_QUERY_DEFAULT:
					return _af_pv_long(_af_units[arg3].defaultSampleFormat);
				default:
					break;
			}
			/* NOTREACHED */
			break;

		case AF_QUERY_SAMPLE_SIZES:
			if (arg3 < 0 || arg3 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;

			switch (arg2)
			{
				case AF_QUERY_DEFAULT:
					return _af_pv_long(_af_units[arg3].defaultSampleWidth);
				default:
					break;
			}
			/* NOTREACHED */
			break;

		case AF_QUERY_COMPRESSION_TYPES:
		{
			int	idx, count;
			int	*buffer;

			if (arg3 < 0 || arg3 >= _AF_NUM_UNITS)
			{
				_af_error(AF_BAD_QUERY,
					"unrecognized file format %d", arg3);
				return AU_NULL_PVLIST;
			}

			switch (arg2)
			{
				case AF_QUERY_VALUE_COUNT:
					count = _af_units[arg3].compressionTypeCount;
					return _af_pv_long(count);

				case AF_QUERY_VALUES:
					count = _af_units[arg3].compressionTypeCount;
					if (count == 0)
						return AU_NULL_PVLIST;

					buffer = (int *) _af_calloc(count, sizeof (int));
					if (buffer == NULL)
						return AU_NULL_PVLIST;

					for (idx = 0; idx < count; idx++)
					{
						buffer[idx] = _af_units[arg3].compressionTypes[idx];
					}

					return _af_pv_pointer(buffer);
			}
		}
		break;
	}

	_af_error(AF_BAD_QUERY, "bad query selector");
	return AU_NULL_PVLIST;
}
Beispiel #10
0
/* ARGSUSED0 */
AUpvlist _afQueryCompression (int arg1, int arg2, int arg3, int arg4)
{
	const CompressionUnit *unit = NULL;

	switch (arg1)
	{
		case AF_QUERY_ID_COUNT:
		{
			int count = 0;
			for (int i = 0; i < _AF_NUM_COMPRESSION; i++)
				if (_af_compression[i].implemented)
					count++;
			return _af_pv_long(count);
		}

		case AF_QUERY_IDS:
		{
			int *buf = (int *) _af_calloc(_AF_NUM_COMPRESSION, sizeof (int));
			if (!buf)
				return AU_NULL_PVLIST;

			int count = 0;
			for (int i = 0; i < _AF_NUM_COMPRESSION; i++)
			{
				if (_af_compression[i].implemented)
					buf[count++] = _af_compression[i].compressionID;
			}
			return _af_pv_pointer(buf);
		}

		case AF_QUERY_IMPLEMENTED:
			unit = _af_compression_unit_from_id(arg2);
			if (!unit)
				return _af_pv_long(0);
			return _af_pv_long(unit->implemented);

		case AF_QUERY_NATIVE_SAMPFMT:
			unit = _af_compression_unit_from_id(arg2);
			if (!unit)
				return AU_NULL_PVLIST;
			return _af_pv_long(unit->nativeSampleFormat);

		case AF_QUERY_NATIVE_SAMPWIDTH:
			unit = _af_compression_unit_from_id(arg2);
			if (!unit)
				return AU_NULL_PVLIST;
			return _af_pv_long(unit->nativeSampleWidth);

		case AF_QUERY_LABEL:
			unit = _af_compression_unit_from_id(arg2);
			if (!unit)
				return AU_NULL_PVLIST;
			return _af_pv_pointer(const_cast<char *>(unit->label));

		case AF_QUERY_NAME:
			unit = _af_compression_unit_from_id(arg2);
			if (!unit)
				return AU_NULL_PVLIST;
			return _af_pv_pointer(const_cast<char *>(unit->shortname));

		case AF_QUERY_DESC:
			unit = _af_compression_unit_from_id(arg2);
			if (!unit)
				return AU_NULL_PVLIST;
			return _af_pv_pointer(const_cast<char *>(unit->name));
	}

	_af_error(AF_BAD_QUERY, "unrecognized query selector %d\n", arg1);
	return AU_NULL_PVLIST;
}
Beispiel #11
0
/* ARGSUSED3 */
AUpvlist _afQueryInstrumentParameter (int arg1, int arg2, int arg3, int arg4)
{
	switch (arg1)
	{
		/* For the following query types, arg2 is the file format. */
		case AF_QUERY_SUPPORTED:
			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			return _af_pv_long(_af_units[arg2].instrumentParameterCount != 0);

		case AF_QUERY_ID_COUNT:
			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			return _af_pv_long(_af_units[arg2].instrumentParameterCount);

		case AF_QUERY_IDS:
		{
			int	count;
			int	*buffer;

			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			count = _af_units[arg2].instrumentParameterCount;
			if (count == 0)
				return AU_NULL_PVLIST;
			buffer = (int *) _af_calloc(count, sizeof (int));
			if (buffer == NULL)
				return AU_NULL_PVLIST;
			for (int i=0; i<count; i++)
				buffer[i] = _af_units[arg2].instrumentParameters[i].id;
			return _af_pv_pointer(buffer);
		}
		/* NOTREACHED */
		break;

		/*
			For the next few query types, arg2 is the file
			format and arg3 is the instrument parameter id.
		*/
		case AF_QUERY_TYPE:
		{
			int	idx;

			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;

			idx = _af_instparam_index_from_id(arg2, arg3);
			if (idx<0)
				return AU_NULL_PVLIST;
			return _af_pv_long(_af_units[arg2].instrumentParameters[idx].type);
		}

		case AF_QUERY_NAME:
		{
			int	idx;

			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			idx = _af_instparam_index_from_id(arg2, arg3);
			if (idx < 0)
				return AU_NULL_PVLIST;
			return _af_pv_pointer(const_cast<char *>(_af_units[arg2].instrumentParameters[idx].name));
		}

		case AF_QUERY_DEFAULT:
		{
			int	idx;

			if (arg2 < 0 || arg2 >= _AF_NUM_UNITS)
				return AU_NULL_PVLIST;
			idx = _af_instparam_index_from_id(arg2, arg3);
			if (idx >= 0)
			{
				AUpvlist	ret = AUpvnew(1);
				AUpvsetparam(ret, 0, _af_units[arg2].instrumentParameters[idx].id);
				AUpvsetvaltype(ret, 0, _af_units[arg2].instrumentParameters[idx].type);
				AUpvsetval(ret, 0, const_cast<AFPVu *>(&_af_units[arg2].instrumentParameters[idx].defaultValue));
				return ret;
			}
			return AU_NULL_PVLIST;
		}
	}

	_af_error(AF_BAD_QUERY, "bad query selector");
	return AU_NULL_PVLIST;
}
Beispiel #12
0
/*
	Parse instrument chunks, which contain information about using
	sound data as a sampled instrument.
*/
static status ParseINST (AFfilehandle file, AFvirtualfile *fh, u_int32_t type,
	size_t size)
{
	_Instrument	*instrument;
	u_int8_t	baseNote;
	int8_t		detune;
	u_int8_t	lowNote, highNote, lowVelocity, highVelocity;
	int16_t		gain;

	u_int16_t	sustainLoopPlayMode, sustainLoopBegin, sustainLoopEnd;
	u_int16_t	releaseLoopPlayMode, releaseLoopBegin, releaseLoopEnd;

	assert(!memcmp(&type, "INST", 4));

	instrument = _af_calloc(1, sizeof (_Instrument));
	instrument->id = AF_DEFAULT_INST;
	instrument->values = _af_calloc(_AF_AIFF_NUM_INSTPARAMS, sizeof (AFPVu));
	instrument->loopCount = 2;
	instrument->loops = _af_calloc(2, sizeof (_Loop));

	file->instrumentCount = 1;
	file->instruments = instrument;

	af_fread(&baseNote, 1, 1, fh);
	af_fread(&detune, 1, 1, fh);
	af_fread(&lowNote, 1, 1, fh);
	af_fread(&highNote, 1, 1, fh);
	af_fread(&lowVelocity, 1, 1, fh);
	af_fread(&highVelocity, 1, 1, fh);
	af_fread(&gain, 2, 1, fh);
	gain = BENDIAN_TO_HOST_INT16(gain);

#ifdef DEBUG
	printf("baseNote/detune/lowNote/highNote/lowVelocity/highVelocity/gain:"
		" %d %d %d %d %d %d %d\n",
		baseNote, detune, lowNote, highNote, lowVelocity, highVelocity,
		gain);
#endif

	instrument->values[0].l = baseNote;
	instrument->values[1].l = detune;
	instrument->values[2].l = lowVelocity;
	instrument->values[3].l = highVelocity;
	instrument->values[4].l = lowNote;
	instrument->values[5].l = highNote;
	instrument->values[6].l = gain;

	instrument->values[7].l = 1;	/* sustain loop id */
	instrument->values[8].l = 2;	/* release loop id */

	af_fread(&sustainLoopPlayMode, sizeof (u_int16_t), 1, fh);
	sustainLoopPlayMode = BENDIAN_TO_HOST_INT16(sustainLoopPlayMode);
	af_fread(&sustainLoopBegin, sizeof (u_int16_t), 1, fh);
	sustainLoopBegin = BENDIAN_TO_HOST_INT16(sustainLoopBegin);
	af_fread(&sustainLoopEnd, sizeof (u_int16_t), 1, fh);
	sustainLoopEnd = BENDIAN_TO_HOST_INT16(sustainLoopEnd);

	af_fread(&releaseLoopPlayMode, sizeof (u_int16_t), 1, fh);
	releaseLoopPlayMode = BENDIAN_TO_HOST_INT16(releaseLoopPlayMode);
	af_fread(&releaseLoopBegin, sizeof (u_int16_t), 1, fh);
	releaseLoopBegin = BENDIAN_TO_HOST_INT16(releaseLoopBegin);
	af_fread(&releaseLoopEnd, sizeof (u_int16_t), 1, fh);
	releaseLoopEnd = BENDIAN_TO_HOST_INT16(releaseLoopEnd);

#ifdef DEBUG
	printf("sustain loop: mode %d, begin %d, end %d\n",
		sustainLoopPlayMode, sustainLoopBegin, sustainLoopEnd);

	printf("release loop: mode %d, begin %d, end %d\n",
		releaseLoopPlayMode, releaseLoopBegin, releaseLoopEnd);
#endif

	instrument->loops[0].id = 1;
	instrument->loops[0].mode = sustainLoopPlayMode;
	instrument->loops[0].beginMarker = sustainLoopBegin;
	instrument->loops[0].endMarker = sustainLoopEnd;

	instrument->loops[1].id = 2;
	instrument->loops[1].mode = releaseLoopPlayMode;
	instrument->loops[1].beginMarker = releaseLoopBegin;
	instrument->loops[1].endMarker = releaseLoopEnd;

	return AF_SUCCEED;
}