#include <string.h>
#include "kernel.h"
#include "swis.h"

#include "IFX.h"
#include "FFX.h"
#include "FChannel.h"
#include "FSong.h"
#include "FSongCtrl.h"
#include "FTables.h"
#include "GlobHdr.h"
#include "Mem.h"
#include "TimLib:Seq.h"
#include "FNotes.h"

extern void ASM_FX_Changer(void);
extern void ASM_FX_Lister(void);

static const _kernel_oserror Err_StillHandler =
{ErrNum_PlayerError, "At least one TimPlayer FX handler is still active."};
static const _kernel_oserror Err_Bad_FX_Handle =
{ErrNum_ParamsError, "Invalid TimPlayer FX handle."};
static const _kernel_oserror Err_TooManyFXHandlers =
{ErrNum_PlayerError, "Too many registered TimPlayer FX handlers."};
static const _kernel_oserror Err_UnknownParamCode =
{ErrNum_ParamsError, "Unknown parameter code."};
static const _kernel_oserror Err_NoActiveNote =
{ErrNum_ParamsError, "No active note on this channel"};
static const _kernel_oserror Err_InvalidChannelNr =
{ErrNum_ParamsError, "Invalid channel number"};
static const _kernel_oserror Err_InvalidNoteNr =
{ErrNum_ParamsError, "Invalid note number"};
static const _kernel_oserror Err_InvalidPolyphony =
{ErrNum_ParamsError, "Invalid maximal polypony"};

/**
 * Initializes the FX handlers and configuration.
 */
const _kernel_oserror* FXs_Init(GlobHdr* g, void* pw)
{
	IGNORE(pw);

	memset(g->fxsptr, 0, sizeof(g->fxsptr));

	return NULL;
}

/**
 * Closes all FX handlers.
 */
void FXs_UnregisterAll(GlobHdr* g)
{
	int i;

	for (i = 0; i < max_fxs; i++)
	{
		if (g->fxsptr[i])
		{
			_kernel_swi_regs r;

			r.r[0] = i;
			swi_FX_Unregister(g, &r);
		}
	}
}

/**
 * Checks if we can finalizes the module with regards to FXs.
 */
const _kernel_oserror* FXs_Finalize(GlobHdr* g, void* pw)
{
	int i;

	IGNORE(pw);

	for (i = 0; i < max_fxs; i++)
	{
		if (g->fxsptr[i])
			return &Err_StillHandler;
	}

	return NULL;
}

/**
 * Registers a new FX handler.
 *
 * In:
 *   r[1] Maximal polyphony [1, 64]
 * Out:
 *   r[0] FX handle
 */
const _kernel_oserror* swi_FX_Register(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx = NULL;
	int id;
	int ioff;
	Channel* pChannel;
	int i;

	// Check parameters
	if ((r->r[1] < 1) || (r->r[1] > 64))
		return &Err_InvalidPolyphony;

	// Find free slot
	for (id = 0; id < max_fxs; id++)
	{
		if (!g->fxsptr[id])
			break;
	}

	if (id >= max_fxs)
		return &Err_TooManyFXHandlers;

	// Allocate handler
	e = CMem_Alloc(g, (void**) &fx, sizeof(*fx));
	if (e) return e;

	g->fxsptr[id] = fx;

	// Initialize handler
	memset(fx, 0, sizeof(*fx));

	fx->pGlb = g;
	fx->Tempo  = 125*8;
	fx->TempoTime = Get_TempoTime(fx->Tempo, Tempo_Base_20Hz);

	fx->Seq.lister = ASM_FX_Lister;
	fx->Seq.changer = ASM_FX_Changer;
	fx->Seq.R12 = fx;
	fx->Seq.maxstreams = r->r[1];
	fx->Seq.volume = 256;

	fx->Notes.Polyphony = r->r[1];
	fx->Notes.MinPitch = 0x0100;
	fx->Notes.MaxPitch = 0x7f00;
	fx->Notes.Volume = fx->Seq.volume;
	fx->Notes.Flags = hnotes_flag_ITenvelopes | hnotes_flag_nocutonhighpitch;

	// Set Channels Volume & Panning
	for (i = 0, pChannel = fx->Channels; i < ifx_max_channels; i++, pChannel++)
	{
		pChannel->id = i;
/* Effects are unused
		// Note volume controller
		pChannel->note_volume.value.max = 256;
		// Column volume controller
		pChannel->channel_volume.value.max = 256;
		// Panning controller
		pChannel->panning.value.max = 256;
		// Autoslides
		pChannel->note_volume.autoslide =
		pChannel->channel_volume.autoslide =
		pChannel->panning.autoslide =
		pChannel->note_pitch.autoslide = cmd_up;
		// Limit pitch range
		pChannel->note_pitch.value.min = fx->Notes.MinPitch;
		pChannel->note_pitch.value.max = fx->Notes.MaxPitch;
*/
		// Defaults
		pChannel->GVolume
			= pChannel->channel_volume.value.start
			= pChannel->channel_volume.value.final
			= 256;
		pChannel->GPanning
			= pChannel->panning.value.start
			= pChannel->panning.value.final
			= 128;
	}

	Notes_ClearAllNotes(&fx->Notes);

	// Register to sequencer
	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	CSeq_RegisterHandler(g, &fx->Seq);

	if (!ioff) _kernel_irqs_on();

	// Return handle
	r->r[0] = id;

	return NULL;
}

/**
 * Finds the FX corresponding to a given id.
 */
static const _kernel_oserror* FX_GetFXHandler(GlobHdr* g, uint32_t id, IFX** pfx)
{
	if ((id >= max_fxs)
	|| !g->fxsptr[id])
		return &Err_Bad_FX_Handle;

	*pfx = g->fxsptr[id];

	return NULL;
}

/**
 * Finds the FX channel corresponding to a given FX id and channel nr.
 */
const _kernel_oserror* FX_GetFXChannel
	( GlobHdr* g
	, uint32_t id
	, uint32_t ch
	, const Channel** ppChannel
	)
{
	if ((id >= max_fxs)
	|| !g->fxsptr[id])
		return &Err_Bad_FX_Handle;

	if (ch >= ifx_max_channels)
		return &Err_InvalidChannelNr;

	*ppChannel = &g->fxsptr[id]->Channels[ch];

	return NULL;
}

/**
 * Unregisters an FX handler.
 *
 * In:
 *   r[0] FX handle
 * Out:
 *   -
 */
const _kernel_oserror* swi_FX_Unregister(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	unsigned int index = r->r[0];
	int ioff;

	e = FX_GetFXHandler(g, index, &fx);
	if (e) return e;

	// Remove handle
	g->fxsptr[index] = NULL;

	// Unregister to sequencer
	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	CSeq_UnregisterHandler(g, &fx->Seq);
	CSeq_ResetMaxWavePercentage(g);

	if (!ioff) _kernel_irqs_on();

	CMem_Free(g, fx);
	CMem_Shrink(g);

	return NULL;
}

/**
 * Updates/Reads the status of an FX handler.
 *
 * In:
 *   r[0] FX handle
 *   r[1] flags
 *   r[2] mask
 * Out:
 *   r[1] flags
 */
const _kernel_oserror* swi_FX_Status(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	unsigned int value = r->r[1];
	unsigned int mask = r->r[2];

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

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

	// Only some flags may be altered
	mask &= ifx_status_altermask;
	fx->Status = (value & mask) | (fx->Status & ~mask);

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

	r->r[1] = fx->Status & ifx_status_readmask;

	return NULL;
}

/**
 * Updates/Reads FX Global settings.
 *
 * In:
 *   r[0] FX handle
 *   r[1] id of global setting
 *   r[2], ... new value of global setting
 * Out:
 *   r[2], ... current values of global setting
 * codes 0   r[2] = volume [0...256..1024], -1 to read
 *       1   r[2] = tempo (in 1/20 Hz) [32*8, 256*8[, -1 to read
 *       2   r[2] = maximal polyphony [1, 64], -1 to read
 *       3   r[2] = min note (in semitones), r[3] = max note (in semitones)
 *           r[2] = -1 to read both limits
 *       4   r[2] = Pre-amplification 1 On, 0 Off, -1 to read
 *       5   r[2] = Use recommanded scale volume, 1 On, 0 Off, -1 to read
 */

const _kernel_oserror* swi_FX_GlobalSettings(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	int ioff;

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

	switch(r->r[1])
	{
		case 0: // Volume
		{
			if ((r->r[2] >= 0) && (r->r[2] <= 1024))
			{
				fx->Seq.volume = r->r[2];

				CSeq_SetMixScale(g);
			}

			r->r[2] = fx->Seq.volume;
		}
		break;
		case 1: // Tempo
		{
			if ((r->r[2] >= 32*8) && (r->r[2] < 256*8))
			{
				fx->Tempo = r->r[2];
				fx->TempoTime = Get_TempoTime(fx->Tempo, Tempo_Base_20Hz);
			}

			r->r[2] = fx->Tempo;
		}
		break;
		case 2: // Polyphony
		{
			if ((r->r[2] >= 1) && (r->r[2] <= 64))
			{
				if (r->r[2] != fx->Notes.Polyphony)
				{
					ioff = _kernel_irqs_disabled();
					if (!ioff) _kernel_irqs_off();

					Notes_SetPolyphony(&fx->Notes, r->r[2]);

					fx->Seq.maxstreams = fx->Notes.Polyphony;

					if (!ioff) _kernel_irqs_on();

					CSeq_SetMixScale(g);
				}
			}

			r->r[2] = fx->Notes.Polyphony;
		}
		break;
		case 3: // Pitch limits
		{
			if ((r->r[2] >= 0)
			&&  (r->r[2] < r->r[3])
			&&  (r->r[3] <= Note_Max))
			{
				fx->Notes.MinPitch = r->r[2] << 8;
				fx->Notes.MaxPitch = r->r[3] << 8;
			}

			r->r[2] = fx->Notes.MinPitch >> 8;
			r->r[3] = fx->Notes.MaxPitch >> 8;
		}
		break;
		case 4: // Pre-amplification
		{
			if (r->r[2] == 0)
				fx->PreAmp = 0;
			else if (r->r[2] == 1)
				fx->PreAmp = Get_PreAmp(fx->Notes.Polyphony);

			r->r[2] = (fx->PreAmp != 0);
		}
		break;
		case 5: // Use recommanded scale volume
		{
			if (r->r[2] == 0)
				fx->Status &= ~ifx_status_scalevolume;
			else if (r->r[2] == 1)
				fx->Status |= ifx_status_scalevolume;

			r->r[2] = ((fx->Status & ifx_status_scalevolume) != 0);
		}
		break;
		default:
			e = &Err_UnknownParamCode;
	}

	return e;
}

/**
 * Updates/Reads FX channel settings.
 *
 * In:
 *   r[0] FX handle
 *   r[1] FX channel nr. [0, 63]
 *   r[2] id of global setting
 *   r[3], ... new value of global setting
 * Out:
 *   r[3], ... current values of global setting
 * codes 0   r[2] = volume [0, 256], -1 to read
 *       1   r[2] = panning [0, 256], 384 for surround, -1 to read
 *       2   r[2] = status
 *            channel described in r[1]
 *              bit 0: alter status: 1 yes, 0 no
 *              bit 1: new status: 1 play, 0 mute
 *            channels other than the one discribed in R1
 *              bit 2: alter status: 1 yes, 0 no
 *              bit 3: new status: 1 play, 0 mute
 */
const _kernel_oserror* swi_FX_ChannelSettings(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	Channel* pChannel;
	Note* pNote;
	int ioff;

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

	if ((r->r[1] < 0) || (r->r[1] >= ifx_max_channels))
		return &Err_InvalidChannelNr;

	pChannel = &fx->Channels[r->r[1]];

	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	pNote = pChannel->pnote;

	switch(r->r[2])
	{
		case 0: // Volume
		{
			if ((r->r[3] >= 0) && (r->r[3] <= 256))
			{
				pChannel->channel_volume.value.start =
				pChannel->channel_volume.value.final = r->r[3];
			}

			r->r[3] = pChannel->channel_volume.value.start;
		}
		break;
		case 1: // Panning
		{
			if ((r->r[3] == 384)
			||  ((r->r[3] >= 0) && (r->r[3] <= 256)))
			{
				pChannel->panning.value.start =
				pChannel->panning.value.final = r->r[3];
			}

			r->r[3] = pChannel->panning.value.start;
		}
		break;
		case 2: // Status
		{
			int i;

			// Treat the other channels
			if (r->r[3] & 4)
			{
				for (i = 0, pChannel = fx->Channels; i < ifx_max_channels; i++, pChannel++)
				{
					if (i != r->r[1])
					{
						if (r->r[3] & 8)
							pChannel->flags &= ~ch_flags_mute;
						else
							pChannel->flags |= ch_flags_mute;
					}
				}
			}

			// Treat the mentionned channel
			pChannel = &fx->Channels[r->r[1]];
			if (r->r[3] & 1)
			{
				if (r->r[3] & 2)
					pChannel->flags &= ~ch_flags_mute;
				else
					pChannel->flags |= ch_flags_mute;
			}
			if (pChannel->flags & ch_flags_mute)
				r->r[3] = 0;
			else
				r->r[3] = 1;
		}
		break;
		default:
			e = &Err_UnknownParamCode;
	}

	if (!ioff) _kernel_irqs_on();

	return e;
}

/**
 * Updates/Reads FX Note settings
 *
 * In:
 *   r[0] FX handle
 *   r[1] FX channel nr. [0, 63]
 *   r[2] code
 *   r[3], ... new values
 * Out:
 *   r[3], ... current values
 *
 * codes  values
 * 0      r[3] = song handle, -1 to read
 *        r[4] = sample nr. [1, nr. of samples]
 * 1      r[3] = note in 1/256th of semitone, &4000 is central note, -1 to read
 * 2      r[3] = volume [0, 256], -1 to read
 * 3      r[3] = filter cutoff [0, 127], -1 to read
 * 4      r[3] = filter resonance [0, 127], -1 to read
 * 5      r[3] = note in 1/256 MOD period, 428*256 is central note, -1 to read
 * 6      r[3] = note in fraction of base frequency, &10000 is central note, -1 to read
 */
const _kernel_oserror* swi_FX_NoteSettings(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	Channel* pChannel;
	Note* pNote;
	int ioff;

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

	if ((r->r[1] < 0) || (r->r[1] >= ifx_max_channels))
		return &Err_InvalidChannelNr;

	pChannel = &fx->Channels[r->r[1]];

	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	pNote = pChannel->pnote;

	if (!pNote)
	{
		e = &Err_NoActiveNote;
	}
	else
	{
		switch(r->r[2])
		{
			case 0: // Note song and intrument id
			{
				ISong* pSong;
				const Sample* pSample;

				if (r->r[3] != -1)
				{
					e = Song_GetSongHeader(g, r->r[3], &pSong);
					if (e) break;
					e = Sample_GetHeader(pSong, r->r[4], &pSample);
					if (e) break;

					// Replace sample by a new one
					fx->Handles[r->r[1]] = r->r[3];
					pChannel->smp_id = r->r[4];
					pChannel->psample = pSample;
					Channel_FixNoteInstrument(pChannel, true, true);
				}
				r->r[3] = fx->Handles[r->r[1]];
				r->r[4] = pChannel->smp_id;
			}
			break;
			case 1: // Note semitone
			{
				if ((r->r[3] >= fx->Notes.MinPitch)
				&&  (r->r[3] < fx->Notes.MaxPitch))
					pNote->ch_pitch = r->r[3];
				else
					r->r[3] = pNote->ch_pitch;
			}
			break;
			case 2: // Note volume
			{
				if ((r->r[3] >= 0) && (r->r[3] <= 256))
				{
					pChannel->note_volume.value.start =
					pChannel->note_volume.value.final = r->r[3];
				}

				r->r[3] = pChannel->note_volume.value.start;
			}
			break;
			case 3: // Note filter cutoff
			{
				if ((r->r[3] >= 0) && (r->r[3] <= 127))
				{
					pNote->cutoff = r->r[3];
				}
				r->r[3] = pNote->cutoff;
			}
			break;
			case 4: // Note filter resonance
			{
				if ((r->r[3] >= 0) && (r->r[3] <= 127))
				{
					pNote->resonance = r->r[3];
				}
				r->r[3] = pNote->resonance;
			}
			break;
			case 5: // Note period
			{
				if (r->r[3] > 0)
				{
					pNote->ch_pitch = Convert_PeriodToTone(r->r[3]);
					if (pNote->ch_pitch < fx->Notes.MinPitch)
						pNote->ch_pitch = fx->Notes.MinPitch;
					else if (pNote->ch_pitch > fx->Notes.MaxPitch)
						pNote->ch_pitch = fx->Notes.MaxPitch;
				}

				r->r[3] = Convert_ToneToPeriod(pNote->ch_pitch);
			}
			break;
			case 6: // Note fraction
			{
				if (r->r[3] > 0)
				{
					pNote->ch_pitch = Convert_FracToTone(r->r[3]);
					if (pNote->ch_pitch < fx->Notes.MinPitch)
						pNote->ch_pitch = fx->Notes.MinPitch;
					else if (pNote->ch_pitch > fx->Notes.MaxPitch)
						pNote->ch_pitch = fx->Notes.MaxPitch;
				}

				r->r[3] = Convert_ToneToFrac(pNote->ch_pitch);
			}
			break;
			default:
				e = &Err_UnknownParamCode;
		}
    }

	if (!ioff) _kernel_irqs_on();

	return e;
}

/**
 * Plays a sample.
 *
 * In:
 *   r[0] FX handle
 *   r[1] FX channel nr. [0, 63] + (mode << 24)
 *   r[2] song handle
 *   r[3] sample nr. [1, nr. of samples]
 *   r[4] mode  pitch expressed as:
 *        0     note nr: note in semitones, &40 is central note (applies reltone, ...)
 *        1     note in 1/256th of semitone, &4000 is central note
 *        2     note in period, &428*256, &4000 is central note
 *        3     note in fraction of base frequency, &10000 is central note
 *   r[5] volume [0, 256], -1 for default volume
 *   r[6] panning [0,256], 384 for surround, -1 for default
 * Out:
 *   -
 */

const _kernel_oserror* swi_FX_PlaySample(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	Channel* pChannel;
	const Sample* pSample;
	ISong* pSong;
	uint32_t channel = r->r[1] & 0xffffff;
	int mode = r->r[1] >> 24;

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

	if (channel >= ifx_max_channels)
		return &Err_InvalidChannelNr;

	if (!mode)
	{
		if ((r->r[4] < 4) || (r->r[4] > Note_Max))
			return &Err_InvalidNoteNr;
	}
	else if (mode > 3)
	{
		return &Err_InvalidNoteNr;
	}

	pChannel = &fx->Channels[channel];

	e = Song_GetSongHeader(g, r->r[2], &pSong);
	if (e) return e;
	e = Sample_GetHeader(pSong, r->r[3], &pSample);
	if (e) return e;

	fx->Handles[channel] = r->r[2];
	pChannel->new_inst_id = pChannel->smp_id   = pChannel->inst_id   = r->r[3];
	pChannel->new_note_id = pChannel->smp_note = pChannel->inst_note
		= mode ? Note_Central : r->r[4];
	pChannel->psample = pSample;
	pChannel->pinstrument = NULL;

	FX_Play(fx, pChannel, r->r[5], r->r[6]);

	Note* pNote = pChannel->pnote;

	if (pNote)
	{
		switch(mode)
		{
			case 1: pNote->ch_pitch = r->r[4]; break;
			case 2: pNote->ch_pitch = Convert_PeriodToTone(r->r[4]); break;
			case 3: pNote->ch_pitch = Convert_FracToTone(r->r[4]); break;
		}
	}

	return e;
}

/**
 * Plays an instrument.
 *
 * In:
 *   r[0] FX handle
 *   r[1] FX channel nr. [0, 63]
 *   r[2] song handle
 *   r[3] instrument nr. [1, nr. of instruments]
 *   r[4] note in semitones, &40 is central note
 *   r[5] volume [0, 256], -1 to use default note volume
 *   r[6] panning [0,256], 384 for surround
 * Out:
 *   -
 */

const _kernel_oserror* swi_FX_PlayInstrument(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	ISong* pSong;
	Channel* pChannel;
	const Instrument* pInstrument;
	const Sample* pSample;

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

	if ((r->r[1] < 0) || (r->r[1] >= ifx_max_channels))
		return &Err_InvalidChannelNr;

	if ((r->r[4] < 4) || (r->r[4] > Note_Max))
		return &Err_InvalidNoteNr;

	pChannel = &fx->Channels[r->r[1]];

	// Song uses instruments?
	e = Song_GetSongHeader(g, r->r[2], &pSong);
	if (e) return e;

	if (pSong->Hdr.Flags & Song_Flag_Instruments)
	{
		e = Instrument_GetHeader(pSong, r->r[3], &pInstrument);
		if (e) return e;

		fx->Handles[r->r[1]] = r->r[2];
		pChannel->new_inst_id = pChannel->inst_id   = r->r[3];
		pChannel->new_note_id = pChannel->inst_note = r->r[4];
		if (pInstrument->pNotesMap)
		{
			const NoteMap* pn = pInstrument->pNotesMap + r->r[4];
			pChannel->smp_id   = pn->SampleNr;
			pChannel->smp_note = pn->Pitch >> 8;
		}
		else
		{
			pChannel->smp_id   = 0;
			pChannel->smp_note = 0;
		}

		e = Sample_GetHeader(pSong, pChannel->smp_id, &pSample);
		if (e)
		{
			pChannel->psample = NULL;
			pChannel->pinstrument = NULL;
			e = NULL;
		}
		else
		{
			pChannel->psample = pSample;
			pChannel->pinstrument = pInstrument;
		}

		FX_Play(fx, pChannel, r->r[5], r->r[6]);
	}
	else return swi_FX_PlaySample(g, r);

	return e;
}

/**
 * Acts on a playing note.
 *
 * In:
 *   r[0] FX handle
 *   r[1] FX channel nr. [0, 63], -1 to apply actions to all channels
 *   r[2] current note action
 *             note cut         * 0
 *             note off         * 1
 *             note offxm       * 2
 *             note fade        * 3
 *             note sustain off * 4
 *             note continue    * 5
 *   r[3] paste notes action
 *             see R2
 * Out:
 *   -
 */

const _kernel_oserror* swi_FX_NoteAction(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	Channel* pChannel;
	Note* pNote;
	int ioff;

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

	if (r->r[1] == -1)
	{
		ioff = _kernel_irqs_disabled();
		if (!ioff) _kernel_irqs_off();

		for (int i = 0; i < ifx_max_channels; i++)
		{
			pChannel = &fx->Channels[i];
			pNote = pChannel->pnote;

			if (pNote)
			{
				Note_PreApplyNoteAction(pNote, r->r[2]);
				Note_ApplyNoteAction(pNote, r->r[2]);
			}

			Notes_PreActOnDuplicateNotes(&fx->Notes, r->r[3], pChannel, 0, 0, 0, Inst_DCT_all);
			Notes_ActOnDuplicateNotes(&fx->Notes, r->r[3], pChannel, 0, 0, 0, Inst_DCT_all);
		}

		if (!ioff) _kernel_irqs_on();

		return NULL;
	}
	else if ((r->r[1] < 0) || (r->r[1] >= ifx_max_channels))
		return &Err_InvalidChannelNr;

	pChannel = &fx->Channels[r->r[1]];
	pNote = pChannel->pnote;

	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	if (pNote)
	{
		Note_PreApplyNoteAction(pNote, r->r[2]);
		Note_ApplyNoteAction(pNote, r->r[2]);
	}

	Notes_PreActOnDuplicateNotes(&fx->Notes, r->r[3], pChannel, 0, 0, 0, Inst_DCT_all);
	Notes_ActOnDuplicateNotes(&fx->Notes, r->r[3], pChannel, 0, 0, 0, Inst_DCT_all);

	if (!ioff) _kernel_irqs_on();

	return e;
}

/**
 * Reads channel info.
 *
 * In:
 *   r[0] FX handle
 *   r[1] FX channel nr. [0, 63]
 *         bit  meaning
 *         24   add instrument/note nr in r[2]
 *   r[2] 16-bit buffer ptr or 0
 *   r[3] buffer len (bytes)
 * Out:
 *   r[2] sample nr (0 if nothing played)
 *         if (bit 24 of flags)
 *         + instrument nr << 8
 *         + inst note nr << 16
 *   r[3] pitch in 1/65536 of default pitch (0 if nothing played)
 *   r[4] if R2 != 0 on input
 *             buffer peak value [0, &8000]
 *          else
 *             note volume [0, &10000]
 *   r[5] if R2 != 0 on input
 *             buffer mean value [0, &8000]
 *          else
 *             channel volume [0, &10000]
 *   r[6] panning [0 (left), 255 (right)], 384 for surround
 */
const _kernel_oserror* swi_FX_ChannelParams(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	Channel* pChannel;
	Note* pNote;
	uint32_t channel = r->r[1] & 0xffffff;

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

	if (channel >= ifx_max_channels)
		return &Err_InvalidChannelNr;

	pChannel = &fx->Channels[channel];

	int ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	pNote = pChannel->pnote;

	if (pNote && (pNote->stream.pos != -1))
	{
		if (r->r[2] && (r->r[3] > 0))
		{
			Note_Monitor nm = {g, pNote, (void*) r->r[2], r->r[3]};

			CNote_FillMonitorBuffer(&nm);
			r->r[4] = nm.peekVolume;
			r->r[5] = nm.meanVolume;
		}
		else
		{
			r->r[4] = pNote->volume;
			r->r[5] = pChannel->GVolume << 8;
		}
		r->r[2] = pNote->smp_id;
		if (r->r[1] & 0x1000000)
		{
			r->r[2] += (pNote->inst_id << 8)
			        +  (pNote->inst_note << 16);
		}
		r->r[3] = pNote->pitch;
		r->r[6] = pNote->panning;
	}
	else
	{
		if (r->r[2]) memset((void*) r->r[2], 0, r->r[3]);
		r->r[2] = 0;
		r->r[3] = 0;
		r->r[4] = 0;
		r->r[5] = pChannel->GVolume << 8;
		r->r[6] = pChannel->GPanning;
	}

	if (!ioff) _kernel_irqs_on();

	return NULL;
}

/**
 * Reads virtual channel info.
 *
 * In:
 *   r[0] FX handle
 *   r[1] virtual note nr.
 *         bit  meaning
 *         24   add instrument/note nr in r[2]
 *   r[2] 16-bit buffer ptr or 0
 *   r[3] buffer len (bytes)
 * Out:
 *  r[7] = -1 if no note playing for note nr >= r[1] else:
 *
 *   r[2] sample nr (0 if nothing played)
 *         if (bit 24 of flags)
 *         + instrument nr << 8
 *         + inst note nr << 16
 *   r[3] pitch in 1/65536 of default pitch (0 if nothing played)
 *   r[4] if R2 != 0 on input
 *             buffer peak value [0, &8000]
 *          else
 *             note volume [0, &10000]
 *   r[5] if R2 != 0 on input
 *             buffer mean value [0, &8000]
 *          else
 *             channel volume [0, &10000]
 *   r[6] panning [0 (left), 255 (right)], 384 for surround
 *   r[7] bits 07: channel nr on which the note was started
 *        bit 31   set if it is a 'past note'
 *        bit 30   set if it uses a filter
 */
const _kernel_oserror* swi_FX_VChannelParams(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IFX* fx;
	Channel* pChannel;
	Note* pNote;
	int ioff;
	uint32_t note = r->r[1] & 0xffffff;

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

	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	if (note < fx->Notes.ActiveNotes)
	{
		pNote = fx->Notes.ActiveNotesArray[note];
		pChannel = pNote->pchannel;

		if (r->r[2] && (r->r[3] > 0))
		{
			Note_Monitor nm = {g, pNote, (void*) r->r[2], r->r[3]};

			CNote_FillMonitorBuffer(&nm);
			r->r[4] = nm.peekVolume;
			r->r[5] = nm.meanVolume;
		}
		else
		{
			r->r[4] = pNote->volume;
			r->r[5] = pChannel->GVolume << 8;
		}
		r->r[2] = pNote->smp_id;
		if (r->r[1] & 0x1000000)
		{
			r->r[2] += (pNote->inst_id << 8)
			        +  (pNote->inst_note << 16);
		}
		r->r[3] = pNote->pitch;
		r->r[6] = pNote->panning;
		r->r[7] = pChannel->id;
		if (pChannel->pnote != pNote)
			r->r[7] |= ~(0x7fffffff);
		if (pNote->stream.flags & mixstream_flag_filter)
			r->r[7] |= 0x40000000;
	}
	else
	{
		r->r[2] = 0;
		r->r[3] = 0;
		r->r[4] = 0;
		r->r[5] = 0;
		r->r[6] = 0;
		r->r[7] = -1;
	}

	if (!ioff) _kernel_irqs_on();

	return NULL;
}
