#include "ISong.h"
#include "Instrument.h"
#include "FSong.h"
#include "FTables.h"
#include "Mem.h"
#include "Effects.h"

static const _kernel_oserror Err_InvalidVibratoType
= { ErrNum_ParamsError, "Invalid vibrato type"};
static const _kernel_oserror Err_InvalidVibratoDepth
= { ErrNum_ParamsError, "Invalid vibrato depth"};
static const _kernel_oserror Err_InvalidVibratoSpeed
= { ErrNum_ParamsError, "Invalid vibrato speed"};
static const _kernel_oserror Err_InvalidVibratoRate
= { ErrNum_ParamsError, "Invalid vibrato rate"};

static const char nil = 0;

/**
 * SWI Pattern_Info
 *
 * In:
 *   r[0] song handle
 *   r[1] pattern nr [0, nr of patterns[
 * Out:
 *   r[2] number of rows in pattern
 *   r[3] pointer to pattern description
 */

static const _kernel_oserror Err_InvalidPatternNr
= { ErrNum_ParamsError, "Invalid pattern number"};

const _kernel_oserror* swi_Pattern_Info(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	ISong* s;
	unsigned int pat_nr = r->r[1];
	const Pattern* pPattern;

	e = Song_GetSongHeader(g, r->r[0], &s);
	if (e) return e;

	if (pat_nr >= s->Hdr.Patterns)
		return &Err_InvalidPatternNr;

	pPattern = &s->Hdr.pPatterns[pat_nr];

	r->r[2] = pPattern->Rows;
	r->r[3] = (int) pPattern->Ptr;

	return NULL;
}

/**
 * SWI Row_Info
 *
 * In:
 *   r[0] song handle
 *   r[1] pattern nr [0, nr of patterns[
 *   r[2] row nr [0, nr of rows[
 * Out:
 *   r[3] pointer to row description
 */

static const _kernel_oserror Err_InvalidRowNr
= { ErrNum_ParamsError, "Invalid row number"};

const _kernel_oserror* swi_Row_Info(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	ISong* s;
	unsigned int pat_nr = r->r[1];
	unsigned int row_nr = r->r[2];
	const Pattern* pPattern;
	const uint8_t* p;

	e = Song_GetSongHeader(g, r->r[0], &s);
	if (e) return e;

	if (pat_nr >= s->Hdr.Patterns)
		return &Err_InvalidPatternNr;

	pPattern = &s->Hdr.pPatterns[pat_nr];

	if (row_nr >= pPattern->Rows)
		return &Err_InvalidRowNr;

	for (p = pPattern->Ptr; row_nr > 0; row_nr--)
		p += 2 + p[0] + (p[1] << 8);

	r->r[3] = (int) p;

	return NULL;
}

/**
 * Sample_FindHeader
 *
 * In:
 *   r[0] song handle
 *   r[1] sample nr with possible addition of 1 << 31
 */

static const _kernel_oserror Err_InvalidSampleNr
= { ErrNum_ParamsError, "Invalid sample number"};

const _kernel_oserror* Sample_GetHeader(const ISong* pSong, unsigned int sample, const Sample** ppSample)
{
	if ((sample < 1) || (sample > pSong->Hdr.Samples))
		return &Err_InvalidSampleNr;

	*ppSample = &pSong->Hdr.pSamples[sample - 1];

	return NULL;
}

static const _kernel_oserror* Sample_FindHeader(const GlobHdr* g, _kernel_swi_regs* r, const Sample** ppSample)
{
	const _kernel_oserror* e;
	unsigned int sample = r->r[1];
	ISong* s;

	e = Song_GetSongHeader(g, r->r[0], &s);
	if (e) return e;

	if (sample & 0x80000000u)
	{
		sample &= ~0x80000000u;

		if ((sample > s->Hdr.Samples)
		&&  (sample <= song_max_samples))
			s->Hdr.Samples = sample;
	}

	return Sample_GetHeader(s, sample, ppSample);
}

/**
 * Instrument_FindHeader
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr with possible addition of 1 << 31
 */

static const _kernel_oserror Err_InvalidInstrumentNr
= { ErrNum_ParamsError, "Invalid instrument number"};

const _kernel_oserror* Instrument_GetHeader(const ISong* pSong, unsigned int inst, const Instrument** ppInst)
{
	if ((inst < 1) || (inst > pSong->Hdr.Instruments))
		return &Err_InvalidInstrumentNr;

	*ppInst = &pSong->Hdr.pInstruments[inst - 1];

	return NULL;
}

static const _kernel_oserror* Instrument_FindHeader(const GlobHdr* g, _kernel_swi_regs* r, const Instrument** ppInst)
{
	const _kernel_oserror* e;
	unsigned int inst = r->r[1];
	ISong* s;

	e = Song_GetSongHeader(g, r->r[0], &s);
	if (e) return e;

	inst &= ~0x80000000u;

	if (inst & 0x80000000u)
	{
		inst &= ~0x80000000u;

		if ((inst > s->Hdr.Instruments)
		&&  (inst <= song_max_instruments))
			s->Hdr.Instruments = inst;
	}

	return Instrument_GetHeader(s, inst, ppInst);
}

/**
 * SWI Sample_Info
 *
 * In:
 *   r[0] song handle
 *   r[1] sample nr [1, nr of samples] + (1<<31) to update
 *   ... if update see below
 * Out:
 *   r[2] sample size (in values, not bytes)
 *   r[3] sample data pointer
 *   r[4] sample flags
 *           unsigned      * &00000001 (converted to signed on loading)
 *           stereo        * &00000002 (not supported)
 *           16bit         * &00000004
 *           loop          * &00000008
 *           loop_bidi     * &00000010
 *           sustain       * &00000020
 *           sustain_bidi  * &00000040
 *           set_panning   * &00000080
 *   r[5] sample name: pointer to 0 terminated string
 *   r[6] sample filename: pointer to 0 terminated string
 */

static const _kernel_oserror Err_SampleReservedFlags
= { ErrNum_ParamsError, "Trying to set sample reserved flags"};

const _kernel_oserror* swi_Sample_Info(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Sample* pSample;

	// Avoid null pointer for caller not check for errors
	r->r[5] = r->r[6] = (int) &nil;

	e = Sample_FindHeader(g, r, (const Sample**) &pSample);
	if (e) return e;

	if (r->r[1] & 0x80000000u)
	{
		if (r->r[4] & ~Smp_Type_VisibleBits)
			return &Err_SampleReservedFlags;

		// Release current data
		pSample->Size = 0;
		if (!(pSample->Type & Smp_Type_ExternalName))
		{
			CMem_Free(g, pSample->pName);
			pSample->pName = NULL;
		}
		if (!(pSample->Type & Smp_Type_ExternalFile))
		{
			CMem_Free(g, pSample->pFilename);
			pSample->pFilename = NULL;
		}
		if (!(pSample->Type & Smp_Type_ExternalPtr))
		{
			CMem_Free(g, pSample->Ptr);
			pSample->Ptr = NULL;
		}

#ifndef MAKEABS
		int ioff = _kernel_irqs_disabled();
		if (!ioff) _kernel_irqs_off();
#endif

		// Set new data
		pSample->Size = r->r[2];
		if (pSample->Size == 0)
			pSample->Ptr = NULL;
		else
			pSample->Ptr = (void*) r->r[3];

		pSample->Type = r->r[4]
		              | Smp_Type_ExternalName
		              | Smp_Type_ExternalFile
		              | Smp_Type_ExternalPtr;
		pSample->pName = (uint8_t*) r->r[5];
		pSample->pFilename = (uint8_t*) r->r[6];

		// Adapt loops
		if (pSample->LoopEnd > pSample->Size)
			pSample->LoopEnd = pSample->Size;
		if (pSample->LoopStart > pSample->LoopEnd)
			pSample->LoopStart = pSample->LoopEnd;
		if (pSample->SustainEnd > pSample->Size)
			pSample->SustainEnd = pSample->Size;
		if (pSample->SustainStart > pSample->SustainEnd)
			pSample->SustainStart = pSample->SustainEnd;

#ifndef MAKEABS
		if (!ioff) _kernel_irqs_on();
#endif
	}

	r->r[2] = pSample->Size;
	r->r[3] = (int) pSample->Ptr;
	r->r[4] = pSample->Type & Smp_Type_VisibleBits;
	r->r[5] = pSample->pName ? (int) pSample->pName : (int) &nil;
	r->r[6] = pSample->pFilename ? (int) pSample->pFilename : (int) &nil;

	return NULL;
}

/**
 * SWI Sample_Misc
 *
 * In:
 *   r[0] song handle
 *   r[1] sample nr [1, nr of samples] + (1<<31) to update
 *   ... if update see below
 * Out:
 *   r[2] sample default volume [0, 256]
 *   r[3] sample scale volume [0, 256]
 *   r[4] sample panning [0, 256], 384 for surround
 *            used only if smp_type_set_panning is on
 *   r[5] sample frequency (Hz)
 *   r[6] sample tone finetune, signed value in 1/2^31 semitones
 *   r[7] sample relative tone (in semitones)
 */

static const _kernel_oserror Err_SampleInvalidDefaultVolume
= { ErrNum_ParamsError, "Invalid sample default volume"};
static const _kernel_oserror Err_SampleInvalidScaleVolume
= { ErrNum_ParamsError, "Invalid sample scale volume"};
static const _kernel_oserror Err_SampleInvalidPanning
= { ErrNum_ParamsError, "Invalid sample panning"};
static const _kernel_oserror Err_SampleInvalidFrequency
= { ErrNum_ParamsError, "Invalid sample frequency"};
static const _kernel_oserror Err_SampleInvalidRelativeTone
= { ErrNum_ParamsError, "Invalid relative tone"};

const _kernel_oserror* swi_Sample_Misc(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Sample* pSample;

	e = Sample_FindHeader(g, r, (const Sample**) &pSample);
	if (e) return e;

	if (r->r[1] & 0x80000000u)
	{
		// Check values
		if ((r->r[2] < 0) || (r->r[2] > 256))
			return &Err_SampleInvalidDefaultVolume;
		if ((r->r[3] < 0) || (r->r[3] > 256))
			return &Err_SampleInvalidScaleVolume;
		if (((r->r[4] < 0) || (r->r[4] > 256)) && (r->r[4] != 384))
			return &Err_SampleInvalidPanning;
		if ((r->r[5] < 256) || (r->r[5] > 256*1024))
			return &Err_SampleInvalidFrequency;
		if ((r->r[7] < -128*256) || (r->r[7] > 127*256))
			return &Err_SampleInvalidRelativeTone;

		// Store values
#ifndef MAKEABS
		int ioff = _kernel_irqs_disabled();
		if (!ioff) _kernel_irqs_off();
#endif

		pSample->DefaultVolume = r->r[2];
		pSample->ScaleVolume   = r->r[3];
		pSample->Panning       = r->r[4];
		pSample->Frequency     = r->r[5];
		pSample->FineTune      = r->r[6];
		pSample->RelTone       = r->r[7];

#ifndef MAKEABS
		if (!ioff) _kernel_irqs_on();
#endif
	}

	r->r[2] = pSample->DefaultVolume;
	r->r[3] = pSample->ScaleVolume;
	r->r[4] = pSample->Panning;
	r->r[5] = pSample->Frequency;
	r->r[6] = pSample->FineTune;
	r->r[7] = pSample->RelTone;

	return NULL;
}

/**
 * SWI Sample_Loops
 *
 * In:
 *   r[0] song handle
 *   r[1] sample nr [1, nr of samples] + (1<<31) to update
 *   ... if update see below
 * Out:
 *   r[2] sample loop start
 *   r[3] sample loop end
 *   r[4] sample sustain loop start
 *   r[5] sample sustain loop end
 */

static const _kernel_oserror Err_SampleInvalidLoop
= { ErrNum_ParamsError, "Invalid sample loop"};
static const _kernel_oserror Err_SampleInvalidSustainLoop
= { ErrNum_ParamsError, "Invalid sample sustain loop"};

const _kernel_oserror* swi_Sample_Loops(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Sample* pSample;

	e = Sample_FindHeader(g, r, (const Sample**) &pSample);
	if (e) return e;

	if (r->r[1] & 0x80000000u)
	{
		// Check values
		if ((r->r[2] < 0)
		||  (r->r[3] < r->r[2])
		||  (r->r[3] > pSample->Size))
			return &Err_SampleInvalidLoop;

		if ((r->r[4] < 0)
		||  (r->r[5] < r->r[4])
		||  (r->r[5] > pSample->Size))
			return &Err_SampleInvalidSustainLoop;

		// Store values
#ifndef MAKEABS
		int ioff = _kernel_irqs_disabled();
		if (!ioff) _kernel_irqs_off();
#endif

		pSample->LoopStart    = r->r[2];
		pSample->LoopEnd      = r->r[3];
		pSample->SustainStart = r->r[4];
		pSample->SustainEnd   = r->r[5];

#ifndef MAKEABS
		if (!ioff) _kernel_irqs_on();
#endif
	}

	r->r[2] = pSample->LoopStart;
	r->r[3] = pSample->LoopEnd;
	r->r[4] = pSample->SustainStart;
	r->r[5] = pSample->SustainEnd;

	return NULL;
}

/**
 * SWI Sample_Vibrato
 *
 * In:
 *   r[0] song handle
 *   r[1] sample nr [1, nr of samples] + (1<<31) to update
 *   ... if update see below
 * Out:
 *   r[2] sample vibrato type
 *           bits 0-2
 *             sin         * &0
 *             square      * &1
 *             ramp        * &2
 *             rampdown    * &3
 *             random      * &4
 *             saw         * &5
 *           bit 3 don't move to start of wave on new note
 *           bit 4 uses sweep instead of rate
 *   r[3] sample vibrato depth [0, 255]
 *   r[4] sample vibrato speed [0, 255]
 *   r[5] sample vibrato rate or sweep [0, 255]
 */

const _kernel_oserror* swi_Sample_Vibrato(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Sample* pSample;

	e = Sample_FindHeader(g, r, (const Sample**) &pSample);
	if (e) return e;

	if (r->r[1] & 0x80000000u)
	{
		// Check values
		if ((r->r[2] & ~0x3f) || ((r->r[2] & 7) > vibrato_type_halfsquare))
			return &Err_InvalidVibratoType;
		if ((r->r[3] < 0) || (r->r[3] > 255))
			return &Err_InvalidVibratoDepth;
		if ((r->r[4] < 0) || (r->r[4] > 255))
			return &Err_InvalidVibratoSpeed;
		if ((r->r[5] < 0) || (r->r[5] > 255))
			return &Err_InvalidVibratoRate;

		// Store values
#ifndef MAKEABS
		int ioff = _kernel_irqs_disabled();
		if (!ioff) _kernel_irqs_off();
#endif

		pSample->Vibrato.Type  = r->r[2];
		pSample->Vibrato.Depth = r->r[3];
		pSample->Vibrato.Speed = r->r[4];
		pSample->Vibrato.Rate  = r->r[5];

#ifndef MAKEABS
		if (!ioff) _kernel_irqs_on();
#endif
	}

	r->r[2] = pSample->Vibrato.Type;
	r->r[3] = pSample->Vibrato.Depth;
	r->r[4] = pSample->Vibrato.Speed;
	r->r[5] = pSample->Vibrato.Rate;

	return NULL;
}

/**
 * SWI Instrument_Info
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] instrument name: pointer to 0 terminated string
 *   r[3] instrument filename: pointer to 0 terminated string
 *   r[4] note mapping table 128 byte pairs (note, sample nr)
 *   r[5] flags
 *           inst_flag_set_panning   * &1
 *   r[6] New note action
 *           note_action_cut      * 0
 *           note_action_off      * 1
 *           note_action_offxm    * 2
 *           note_action_fade     * 3
 *           note_action_continue * 4
 *   r[7] Duplicate check type
 *           inst_DCT_off         * 0
 *           inst_DCT_note        * 1
 *           inst_DCT_sample      * 2
 *           inst_DCT_instrument  * 3
 *   r[8] Duplicate note action
 *           see r[6]
 */
const _kernel_oserror* swi_Instrument_Info(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;

	// Avoid null pointer for caller not check for errors
	r->r[2] = r->r[3] = (int) &nil;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	r->r[2] = pInst->pName ? (int) pInst->pName : (int) &nil;
	r->r[3] = pInst->pFilename ? (int) pInst->pFilename : (int) &nil;
	r->r[4] = (int) pInst->pNotesMap;
	r->r[5] = pInst->Flags;
	r->r[6] = pInst->NewNoteAction;
	r->r[7] = pInst->DuplicateCheckType;
	r->r[8] = pInst->DuplicateNoteAction;

	return NULL;
}

/**
 * SWI Instrument_Volume
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] scale volume [0, 256]
 *   r[3] fadeout volume [0, 65536]
 *   r[4] swing, weighted random variation [0, 255]
 */

const _kernel_oserror* swi_Instrument_Volume(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	r->r[2] = (pInst->pNotesMap && pInst->pNotesMap[Note_Central].Volume) ? pInst->pNotesMap[Note_Central].Volume + 1 : 0;
	r->r[3] = pInst->FadeoutVolume;
	r->r[4] = pInst->VolumeSwing;

	return NULL;
}

/**
 * SWI Instrument_Panning
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] panning [0, 256], 384 for surround
 *   r[3] swing, weighted random variation [0,255]
 *   r[4] pitch-pan seperation [0, 128]
 *   r[5] pitch-pan central note [4 (C-0), 123 (B-9)]
 */
const _kernel_oserror* swi_Instrument_Panning(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	r->r[2] = pInst->Panning;
	r->r[3] = pInst->PanningSwing;
	r->r[4] = pInst->PitchPanSep;
	r->r[5] = pInst->PitchPanCenter;

	return NULL;
}

/**
 * SWI Instrument_Filter
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] cutoff frequency (Hz)
 *   r[3] filter quality [0,255]
 */

static const int CutoffFreqs[128] =
{   821,   846,   870,   896,   922,   949,   977,  1006
,  1035,  1065,  1097,  1129,  1162,  1196,  1231,  1267
,  1304,  1342,  1382,  1422,  1464,  1507,  1551,  1597
,  1643,  1692,  1741,  1792,  1845,  1899,  1954,  2012
,  2071,  2131,  2194,  2258,  2324,  2392,  2462,  2535
,  2609,  2685,  2764,  2845,  2928,  3014,  3103,  3194
,  3287,  3384,  3483,  3585,  3690,  3798,  3909,  4024
,  4142,  4263,  4388,  4517,  4649,  4785,  4925,  5070
,  5218,  5371,  5529,  5691,  5857,  6029,  6206,  6388
,  6575,  6768,  6966,  7170,  7380,  7596,  7819,  8048
,  8284,  8527,  8777,  9034,  9298,  9571,  9851, 10140
, 10437, 10743, 11058, 11382, 11715, 12059, 12412, 12776
, 13150, 13536, 13932, 14340, 14761, 15193, 15638, 16097
, 16568, 17054, 17554, 18068, 18597, 19142, 19703, 20281
, 20875, 21487, 22116, 22764, 23431, 24118, 24825, 25552
, 26301, 27072, 27865, 28681, 29522, 30387, 31277, 32194
};

uint32_t Convert_FreqToCutoff(uint32_t freq)
{
	uint32_t min = 0, max = 127, avg = 63;

	while (min < avg)
	{
		if (CutoffFreqs[avg] < freq)
			min = avg;
		else
			max = avg;
		avg = (min + max) / 2;
	}

	return max;
}

const _kernel_oserror* swi_Instrument_Filter(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	r->r[2] = (pInst->FilterCutoff & 0x80) ? pInst->FilterCutoff & 0x7f : 0x7f;
	r->r[3] = (pInst->FilterResonance & 0x80) ? pInst->FilterResonance & 0x7f : 0;
	r->r[2] = ((r->r[2] != 0x7f) || r->r[3]) ? CutoffFreqs[r->r[2]] : 0;

	return NULL;
}

/**
 * SWI Instrument_VolumeEnvelope
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] nr of points
 *   r[3] table ptr
 *   r[4] flags
 *           envelope_flag_on        * 1
 *           envelope_flag_sustain   * 2
 *           envelope_flag_loop      * 4
 *           envelope_flag_carry     * 8
 *   r[5] loop start point
 *   r[6] loop end point
 *   r[7] sustain start point
 *   r[8] sustain end point
 */

const _kernel_oserror* swi_Instrument_VolumeEnvelope(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;
	const Envelope* pEnv;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	pEnv = &pInst->VolumeEnvelope;
	r->r[2] = pEnv->Points;
	r->r[3] = (int) pEnv->pTable;
	r->r[4] = pEnv->Flags;
	r->r[5] = pEnv->LoopStart;
	r->r[6] = pEnv->LoopEnd;
	r->r[7] = pEnv->SustainStart;
	r->r[8] = pEnv->SustainEnd;

	return NULL;
}

/**
 * SWI Instrument_PanningEnvelope
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] nr of points
 *   r[3] table ptr
 *   r[4] flags
 *           envelope_flag_on        * 1
 *           envelope_flag_sustain   * 2
 *           envelope_flag_loop      * 4
 *           envelope_flag_carry     * 8
 *   r[5] loop start point
 *   r[6] loop end point
 *   r[7] sustain start point
 *   r[8] sustain end point
 */

const _kernel_oserror* swi_Instrument_PanningEnvelope(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;
	const Envelope* pEnv;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	pEnv = &pInst->PanningEnvelope;
	r->r[2] = pEnv->Points;
	r->r[3] = (int) pEnv->pTable;
	r->r[4] = pEnv->Flags;
	r->r[5] = pEnv->LoopStart;
	r->r[6] = pEnv->LoopEnd;
	r->r[7] = pEnv->SustainStart;
	r->r[8] = pEnv->SustainEnd;

	return NULL;
}

/**
 * SWI Instrument_PitchEnvelope
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] nr of points
 *   r[3] table ptr
 *   r[4] flags
 *           envelope_flag_on        * 1
 *           envelope_flag_sustain   * 2
 *           envelope_flag_loop      * 4
 *           envelope_flag_carry     * 8
 *   r[5] loop start point
 *   r[6] loop end point
 *   r[7] sustain start point
 *   r[8] sustain end point
 */

const _kernel_oserror* swi_Instrument_PitchEnvelope(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;
	const Envelope* pEnv;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	pEnv = &pInst->PitchEnvelope;
	r->r[2] = pEnv->Points;
	r->r[3] = (int) pEnv->pTable;
	r->r[4] = pEnv->Flags;
	r->r[5] = pEnv->LoopStart;
	r->r[6] = pEnv->LoopEnd;
	r->r[7] = pEnv->SustainStart;
	r->r[8] = pEnv->SustainEnd;

	return NULL;
}

/**
 * SWI Instrument_FilterEnvelope
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments]
 * Out:
 *   r[2] nr of points
 *   r[3] table ptr
 *   r[4] flags
 *           envelope_flag_on        * 1
 *           envelope_flag_sustain   * 2
 *           envelope_flag_loop      * 4
 *           envelope_flag_carry     * 8
 *   r[5] loop start point
 *   r[6] loop end point
 *   r[7] sustain start point
 *   r[8] sustain end point
 */

const _kernel_oserror* swi_Instrument_FilterEnvelope(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;
	const Envelope* pEnv;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	pEnv = &pInst->FilterEnvelope;
	r->r[2] = pEnv->Points;
	r->r[3] = (int) pEnv->pTable;
	r->r[4] = pEnv->Flags;
	r->r[5] = pEnv->LoopStart;
	r->r[6] = pEnv->LoopEnd;
	r->r[7] = pEnv->SustainStart;
	r->r[8] = pEnv->SustainEnd;

	return NULL;
}

/**
 * SWI Instrument_Vibrato
 *
 * In:
 *   r[0] song handle
 *   r[1] instrument nr [1, nr of instruments] + (1<<31) to update
 *   ... if update see below
 * Out:
 *   r[2] instrument vibrato type
 *           bits 0-2
 *             sin         * &0
 *             square      * &1
 *             ramp        * &2
 *             rampdown    * &3
 *             random      * &4
 *             saw         * &5
 *           bit 3 don't move to start of wave on new note
 *           bit 4 uses sweep instead of rate
 *   r[3] instrument vibrato depth [0, 255]
 *   r[4] instrument vibrato speed [0, 255]
 *   r[5] instrument vibrato rate or sweep [0, 255]
 */

const _kernel_oserror* swi_Instrument_Vibrato(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	Instrument* pInst;

	e = Instrument_FindHeader(g, r, (const Instrument**) &pInst);
	if (e) return e;

	if (r->r[1] & 0x80000000u)
	{
		// Check values
		if ((r->r[2] & ~0x3f) || ((r->r[2] & 7) > vibrato_type_halfsquare))
			return &Err_InvalidVibratoType;
		if ((r->r[3] < 0) || (r->r[3] > 255))
			return &Err_InvalidVibratoDepth;
		if ((r->r[4] < 0) || (r->r[4] > 255))
			return &Err_InvalidVibratoSpeed;
		if ((r->r[5] < 0) || (r->r[5] > 255))
			return &Err_InvalidVibratoRate;

		// Store values
#ifndef MAKEABS
		int ioff = _kernel_irqs_disabled();
		if (!ioff) _kernel_irqs_off();
#endif

		pInst->Vibrato.Type  = r->r[2];
		pInst->Vibrato.Depth = r->r[3];
		pInst->Vibrato.Speed = r->r[4];
		pInst->Vibrato.Rate  = r->r[5];

#ifndef MAKEABS
		if (!ioff) _kernel_irqs_on();
#endif
	}

	r->r[2] = pInst->Vibrato.Type;
	r->r[3] = pInst->Vibrato.Depth;
	r->r[4] = pInst->Vibrato.Speed;
	r->r[5] = pInst->Vibrato.Rate;

	return NULL;
}
