/*
   Risc os Digital Signal Processor
  (C) 2014-2019 A.V.Bartram

  v0.87

  THIS SOFTWARE IS PROVIDED  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL A.V.Bartram BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL
  ,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO
  ),PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS
  ;OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "kernel.h"
#include "swis.h"
#include "cmodulehdr.h"
#include "synth.h"

extern void buffer_fill_veneer(void);


/*
 * RDSP : RISC OS Digital Signal Processor
 * ---------------------------------------
 *
 *
 */

/* SharedSound SWI numbers */
#define SharedSound_InstallHandler	0x4B440
#define SharedSound_SampleRate		0x4B446
#define SharedSound_RemoveHandler	0x4B441

#define  TM                     "RSynth"
#define  UNKNOWN_CMD_SERVICE    4
#define  UNUSED(x)              (x = x)

//static _kernel_oserror tm_error = { 0x88000, "" };

static int rstart_refcount = 0;
static int sound_handler_id;
static int unknown_cmd_count;
static int other_service_call_count;

// Instance of the syntheiser
RISC_SYNTH g_synth;

// TODO: Rate must be a member of the synthesiser as well as a timestretch
// constant.
// Deferred past version 1.00 : Requires 44khz audio
static int g_rate = 0;

static int g_initialised = 0;

// For diagnostics
static int g_verbose = 0;

// For increased BBC Micro compatibility
// n.b. At the moment this will be limited to the envelope command
//      Sound channel zero and SOUND command have reasonable defaults.
static int g_legacy = 0;

// SOUND 1,-,100,100 override
static int g_bell = 0;

// Move to synth.h
typedef unsigned char* BYTE;

// Sound Queue writer
int QueueSound(int channel,SYNTH_CHANNEL *synthChannel)
{
  extern RISC_SYNTH g_synth;
  int gap = 0;
  int success = 0;

  // Take a copy of reader to avoid race conditions
  // that could result in a failure to queue sounds.
  int r = g_synth.m_queue[channel].m_reader;
  if (g_synth.m_queue[channel].m_writer <
      r)
  {
    gap = (MAX_QUEUE_BUFFER - r) +
          g_synth.m_queue[channel].m_writer;
  }
  else
  {
    gap = g_synth.m_queue[channel].m_writer - r;
  }

  // As only this function writes to the queue - the sound interrupt
  // cannot cause a race condition with this function
  if (gap < (MAX_QUEUE_BUFFER-1))
  {
    int w = 0;
    success = 1;
    // Because the reader can attempt at read interrupt at any time
    // it is important that writing to the channel queue occurs
    // ahead of updating the writer pointer. Otherwise incomplete
    // copies may occur.
    w = g_synth.m_queue[channel].m_writer + 1;
    if (w == MAX_QUEUE_BUFFER)
    {
      w = 0;
    }
    memcpy(&(g_synth.m_queue[channel].m_ringBuffer[w]),
           synthChannel,
           sizeof(SYNTH_CHANNEL));
    // Commit to the queue so it is visible to the reader interrupt
    g_synth.m_queue[channel].m_writer = w;
  }
  return success;
}

void ChannelCommit( SYNTH_CHANNEL *dest,SYNTH_CHANNEL *src)
{
  // Need to avoid upsetting the ADSR state - should be a flag
  // really - this judder / drop out is still there but should be improved

  // ADSR envelope stage is used to trigger channel processing.
  int commit = src->m_adsr.m_stage;
  int osclkp = dest->m_dco.m_osc_lookup;
  src->m_adsr.m_stage = dest->m_adsr.m_stage; // preserve this state to avoid hard setting
                              // channels off causing drop outs
  memcpy(dest, src, sizeof(SYNTH_CHANNEL));
  dest->m_adsr.m_stage = commit;
  if (src->m_wave != 0)
  {
    dest->m_dco.m_osc_lookup = osclkp;
  }
}

// Effect Presets
void SetReverbHall(unsigned char param)
{
  // Implement range 1.0 to 1.5 multiplier on base timing value
  // 44.1 = samples per millisecond.
  double range = (((double)(param))/510.0) + 1.0;
  int i = 0;
  g_synth.m_effects.m_reverb.m_line[0].m_record = (U32)(800.0 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[1].m_record = (U32)(746.426 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[2].m_record = (U32)(696.440 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[3].m_record = (U32)(649.802 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[4].m_record = (U32)(606.287 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[5].m_record = (U32)(565.605 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[6].m_record = (U32)(527.803 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[7].m_record = (U32)(492.458 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[8].m_record = (U32)(459.479 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[9].m_record = (U32)(428.709 * 44.1 * range);
  for (i = 0; i < 10; ++i)
    g_synth.m_effects.m_reverb.m_line[i].m_play = 0;

  // No resonance.
  // cut off = 180 / 255
  g_synth.m_effects.m_reverb.m_lpf.cutoff = 230;
  g_synth.m_effects.m_reverb.m_numLine = 10;

}

void SetReverbRoom(unsigned char param)
{
  // Implement range 1.0 to 1.5 multiplier on base timing value
  // 44.1 = samples per millisecond.
  double range = (((double)(param))/510.0) + 1.0;
  int i;

  g_synth.m_effects.m_reverb.m_line[0].m_record = (U32)(100.0 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[1].m_record = (U32)(89.0899 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[2].m_record = (U32)(79.3701 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[3].m_record = (U32)(70.7107 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[4].m_record = (U32)(62.9961 * 44.1 * range);
  g_synth.m_effects.m_reverb.m_line[5].m_record = (U32)(56.1231 * 44.1 * range);

  for (i = 0; i < 10; ++i)
  g_synth.m_effects.m_reverb.m_line[i].m_play = 0;

  // No resonance.
  // cut off = 180 / 255
  g_synth.m_effects.m_reverb.m_lpf.cutoff = 230;
  g_synth.m_effects.m_reverb.m_numLine = 6;

}



// Public functions
int HandleRStart(void)
{
  rstart_refcount++;

  if (g_initialised)
  {
    if (g_verbose)
      printf("Already '*rstart'ed. Reference count increased\n");

    return 0;
  }

  extern RISC_SYNTH g_synth;
  _kernel_swi_regs regs;
  _kernel_oserror *err;

  int i;
    // Ensure synth channel is zero - TODO - call synthInit
  memset(&g_synth, 0, sizeof(RISC_SYNTH));
  g_synth.m_compressor = 255; // OFF - SHOULD START AT 1
  g_synth.m_masterVolume = 128;
  g_synth.m_compressorSpeed = 4;
  for (i = 0; i < MAX_POLY; ++i)
  {
    g_synth.m_channel[i].m_lpf.cutoff = 255;
    g_synth.m_channel[i].m_lpf.resonance = 0;
    g_synth.m_channel[i].m_dco.m_sample.m_data = NULL;
    g_synth.m_channel[i].m_dco.m_sample.m_dataLength = 0;
    g_synth.m_channel[i].m_prng = 1;     // Maybe move to initialisation
    g_synth.m_channel[i].m_noise_throttle = 0;
  }
    g_synth.tonetable = _toneTable;
    g_synth.sampleFreqTable = _sampleFreqTable;
    for (i = 0; i < 256; ++i)
      g_synth.m_sample[i].m_data = NULL;

    // Default to 1/2 second and 100/256 feedback
    g_synth.m_effects.m_delayLine.m_record = 50 * 441;
    g_synth.m_effects.m_delayLine.m_play = 0;
    g_synth.m_effects.m_delayLine.m_feedback = 100;
    // Defaults
    g_synth.m_effects.m_chorus.m_lfoRate = 2;
    g_synth.m_effects.m_chorus.m_lfoSpeed = 131072;
    g_synth.m_effects.m_chorus.m_lfoPolarity = 1; // positiev
    g_synth.m_effects.m_chorus.m_lfoDepth = 249984; // i.e. target speed
    g_synth.m_effects.m_chorus.m_line.m_record = 3 * 441 * 16384 * 8;
    g_synth.m_effects.m_chorus.m_line.m_play = 0;

    // Set reverb algorithm to hall default
    SetReverbHall(0);


	/* Try to register sharedsound handler, return nonzero on failure */

	/* First load SharedSound if it isn't already loaded
	   A *command is the easiest way to do this (usually in your programs !Run file, but this demo doesn't have one) */
	system("RMEnsure SharedSound 0.00 IfThere System:Modules.SSound Then RMLoad System:Modules.SSound");
	/* Now check whether it is loaded, so we can print a more friendly error message than 'SWI xxx not found' */
	regs.r[0] = 18;
	regs.r[1] = (int) "SharedSound";
	if ( _kernel_swi(OS_Module,&regs,&regs))
	{
		printf("Error: SharedSound module not loaded, and not in System:Modules!\n");
		return 1;
	 }

  /* Request 44 khz audio. This should succeed on all modern platforms */
  /* So in most cases scaling our output buffers will not be required */
	/* Get sample rate we have been assigned */
	regs.r[0] = 0; // Override system handler
	regs.r[1] = 44100*1024;
	_kernel_swi(SharedSound_SampleRate,&regs,&regs);
	g_rate = regs.r[1];

    if (g_verbose)
	  printf("RDSP: Shared sound running at %d / 1024 Hz\n", regs.r[1]);

    if (regs.r[1] != 45158400)
    {
      printf("RDSP: Warning! RDSP currently only supports 44 Khz audio\n");
      printf("RDSP:          Sounds and samples will not play at correct pitch.\n");
      printf("RDSP: RISC OS has returned %d / 1024 Hz for shared sound\n", regs.r[1]);
    }

	/* Now we can register our handler */
	regs.r[0] = (int) buffer_fill_veneer;
	regs.r[1] = (int)&g_synth;
	regs.r[2] = 1;
	regs.r[3] = (int) "RDSP Beta";
	regs.r[4] = 0;
	if ( _kernel_swi(SharedSound_InstallHandler,&regs,&regs))
	{
		printf("Error: SharedSound_InstallHandler returned error %d, %s\n",err->errnum,err->errmess);
		return 1;
	}
	sound_handler_id = regs.r[0];
	/* Install error handlers */
//	atexit(shutdown_sharedsound);
//	regs.r[0] = 1;
//	regs.r[1] = (int) error_handler;
//	regs.r[2] = 0;
//	_kernel_swi(OS_Claim,&regs,&regs);


   g_initialised = 1;

   if (g_verbose)
     printf("Shared sound installed ok\n");

	return 0;
}

void HandleRStop(void)
{

	 _kernel_swi_regs regs;
	 int i = 0;
	 extern RISC_SYNTH g_synth;

    if (g_initialised == 0)
      return;

    rstart_refcount--;
/*    if (rstart_refcount > 0)
    {
      if (g_verbose)
        printf("Rstop reference count decreased. Now %d\n", rstart_refcount);
        return;
    }*/

	/* Deregister sharedsound handler */
	if(sound_handler_id)
	{
		regs.r[0] = sound_handler_id;
		_kernel_swi(SharedSound_RemoveHandler,&regs,&regs);
		sound_handler_id = 0;
	 }


	regs.r[0] = 7;
	regs.r[1] = (int)&rosesound_entry;
	regs.r[2] = 0;
//	_kernel_swi(OS_Release,&regs,&regs);

	/* Remove error handler */
//	regs.r[0] = 1;
//	regs.r[1] = (int) error_handler;
//	regs.r[2] = 0;
 //	_kernel_swi(OS_Release,&regs,&regs);

 // Free up any samples that were loaded
 for (i = 0; i < 256; ++i)
 {
   if (g_synth.m_sample[i].m_data != NULL)
   {
     free(g_synth.m_sample[i].m_data);
     g_synth.m_sample[i].m_data = NULL;
   }
 }

  if (g_verbose)
	   printf("Shut down\n");
  g_initialised = 0;
}


int HandleRSound(_kernel_swi_regs *registers, SYNTH_CHANNEL *synthChannelPtr)
{
  extern RISC_SYNTH g_synth;
  SYNTH_CHANNEL synthChannel;
  int    error = 0;
  int    channel = (registers->r[0]-1) & MAX_POLY_MASK;
  // Preserve existing channel filter settings
  int    cutoff = g_synth.m_channel[channel].m_lpf.cutoff;
  int    res =  g_synth.m_channel[channel].m_lpf.resonance;

  memset(&synthChannel, 0, sizeof(SYNTH_CHANNEL));
  if (synthChannelPtr)
  {
    synthChannel.m_lpf.cutoff = synthChannelPtr->m_lpf.cutoff;
    synthChannel.m_lpf.resonance = synthChannelPtr->m_lpf.resonance;
  }
  else
  {
    synthChannel.m_lpf.cutoff = cutoff;
    synthChannel.m_lpf.resonance = res;
  }

  // *rsound <channel> <flags> <wave | instrument> <volume> <pitch> <duration>

  synthChannel.m_mode = registers->r[1];

  // Check if a waveform is required or an instrument/envelope is to
  // be played.
  if (isdigit(((char*)registers->r[2])[0]))
  {
    int wave =  atoi((char*)registers->r[2]);

    if (wave <= 0xff)
      synthChannel.m_wave = wave;
    else
    {
      // Is a sample required to be played?
      wave = wave - 0x100;
      if (wave < 0x200)
      {
        synthChannel.m_wave = 0;
        synthChannel.m_dco.m_osc_lookup = 0;
        synthChannel.m_dco.m_sample_lookup_msw = 0;
        synthChannel.m_dco.m_sample.m_dataLength = g_synth.m_sample[wave].m_length;
        synthChannel.m_dco.m_sample.m_data = g_synth.m_sample[wave].m_data;
        synthChannel.m_dco.m_sample.m_loop = g_synth.m_sample[wave].m_loop;
        synthChannel.m_dco.m_sample.m_loopBack = g_synth.m_sample[wave].m_loopBack;

      }
    }

    synthChannel.m_adr.m_flags = 0;
    synthChannel.m_adr.m_counterSpeed = 0;
    synthChannel.m_amp = registers->r[3];

    synthChannel.m_tone = registers->r[4];
    synthChannel.m_adsr.m_stage = 0;
    synthChannel.m_adsr.m_stageCounter = 0;
    synthChannel.m_adsr.m_endState = 3;
    synthChannel.m_adsr.m_counterSpeed = 1;
    synthChannel.m_adsr.m_numADSR[0] = 1;
    synthChannel.m_adsr.m_numADSR[1] = 0;
    synthChannel.m_adsr.m_numADSR[2] = registers->r[5];
    synthChannel.m_adsr.m_numADSR[3] = 1;
    synthChannel.m_adsr.m_stepADSR[0] = 1;
    synthChannel.m_adsr.m_stepADSR[1] = 0;
    synthChannel.m_adsr.m_stepADSR[2] = 0;
    synthChannel.m_adsr.m_stepADSR[3] = 1;
    synthChannel.m_adsr.m_targetADSR[0] = 0;
    synthChannel.m_adsr.m_targetADSR[1] = 0;
    synthChannel.m_adsr.m_targetADSR[2] = 0;
    synthChannel.m_adsr.m_targetADSR[3] = 0;

  }
  else
  {

    // Find the instrument in the table
    int found = 0;
    int i = 0;
    while ((!found) && (i < 256))
    {
      if (strcmp(g_synth.m_instrument[i].m_name, (char*)registers->r[2]) == 0)
        found = 1;
      else
        ++i;
    }

    if (found)
    {
      // Attack, decay and release are portions of duration
      int duration =  registers->r[5];  // map to sustain
      int volume = registers->r[3];
      int targetVolume = 0;
      int attackDelta = 0;
      int attackSteps = 0;
      int attackTarget = 0;
      int decayDelta = 0;
      int decaySteps = 0;
      int decayTarget = 0;
      int waveform = g_synth.m_instrument[i].m_wave;
      synthChannel.m_amp = 0;
      if (waveform <= 255)
        synthChannel.m_wave = waveform;
      else
      {
        // Waveform corresponds to sample number - 0x100
        int wave = waveform - 0x100;
        synthChannel.m_wave = 0;
        synthChannel.m_dco.m_osc_lookup = 0;
        synthChannel.m_dco.m_sample_lookup_msw = 0;
        synthChannel.m_dco.m_sample.m_dataLength = g_synth.m_sample[wave].m_length;
        synthChannel.m_dco.m_sample.m_data = g_synth.m_sample[wave].m_data;
        synthChannel.m_dco.m_sample.m_loop = g_synth.m_sample[wave].m_loop;
        synthChannel.m_dco.m_sample.m_loopBack = g_synth.m_sample[wave].m_loopBack;
      }
      synthChannel.m_tone = registers->r[4];
      synthChannel.m_adsr.m_counterSpeed = 1;
      synthChannel.m_adsr.m_stage = 0;
      synthChannel.m_adsr.m_stageCounter = 0;
      synthChannel.m_adsr.m_endState = 4;

      synthChannel.m_adsr.m_flags = g_synth.m_instrument[i].m_options;
      // Four stage envelope

      // I need a logarithmic curve here
      // the division is lossy and you can't have a plucking sound
      // unless there is a curve.
      // Also: ADSR uses a fixed point fraction to operate with 16 bits
      //       for the fractional part of the number.

      if (g_synth.m_instrument[i].m_decay >
          g_synth.m_instrument[i].m_sustain)
          targetVolume = (g_synth.m_instrument[i].m_decay * volume) >> 8;
      else
          targetVolume = (g_synth.m_instrument[i].m_sustain * volume) >> 8;

      if ((targetVolume != 0) && (g_synth.m_instrument[i].m_attack != 0))
      {
        // The attack and decay should not be volume levels
        // but relative to volume
        // I have changed the ADSR (not asr)
        // to allow target volumes to be reached
        // before changing state.


        attackDelta = (targetVolume << 16 ) / g_synth.m_instrument[i].m_attack;
        if (attackDelta == 0)
          attackDelta = 1; // Not likely now.

        attackTarget = targetVolume;

        attackSteps = MAX_STEPS;
      }
      else
      {
        attackDelta = volume << 16;
        attackSteps = 1;
      }


      // ADSR runs with a 16 bit fractional step stored in accumulators
      synthChannel.m_adsr.m_stepADSR[0] = attackDelta;
      synthChannel.m_adsr.m_numADSR[0] = attackSteps;
      synthChannel.m_adsr.m_targetADSR[0] = attackTarget;

      // Decay is rate. Sustain is proportion of volume
      if (g_synth.m_instrument[i].m_decay >
                   g_synth.m_instrument[i].m_sustain)
      {

        decaySteps = MAX_STEPS;

        decayDelta = -65536;


        // Sustain is a proportion of volume
        decayTarget = (volume * g_synth.m_instrument[i].m_sustain) >> 8;

      }
      else
      {
        decayTarget = 0;
        decayDelta = 0;
        decaySteps = 0;
      }


      synthChannel.m_adsr.m_stepADSR[1] =decayDelta;
      synthChannel.m_adsr.m_numADSR[1] = decaySteps;
      synthChannel.m_adsr.m_targetADSR[1] = decayTarget;

      synthChannel.m_adsr.m_stepADSR[2] = 0;
      synthChannel.m_adsr.m_numADSR[2] = duration;
      synthChannel.m_adsr.m_targetADSR[2] = 0;

      synthChannel.m_adsr.m_numADSR[3] = g_synth.m_instrument[i].m_release * 2;
      synthChannel.m_adsr.m_targetADSR[3] = 0;
      {

        // volume * g_synth.m_instrument[i].m_sustain * 256

        // 256 = 65536   // allowing for volume - min = 256
        // 1   = 16777216 (up to depending on volume) - max = 65536
        int timestep =  g_synth.m_instrument[i].m_release == 0 ? 1 : g_synth.m_instrument[i].m_release ;
        int susvolume = (volume * g_synth.m_instrument[i].m_sustain) * 256;
        int stepSize = (susvolume / timestep) >> 1;

        if (stepSize == 0)
          stepSize = 1;

        synthChannel.m_adsr.m_stepADSR[3] = stepSize * -1;

      }

      // 3 Stage Envelope
      synthChannel.m_adr.m_stage = 0;
      synthChannel.m_adr.m_stageCounter = 0;
      synthChannel.m_adr.m_endState = 4;

      synthChannel.m_adr.m_counterSpeed = g_synth.m_instrument[i].m_steptime;
      synthChannel.m_adr.m_flags = g_synth.m_instrument[i].m_options;
      synthChannel.m_adr.m_stepADSR[0] = g_synth.m_instrument[i].m_asrAttackDelta;
      synthChannel.m_adr.m_stepADSR[1] = 0;
      synthChannel.m_adr.m_stepADSR[2] = g_synth.m_instrument[i].m_asrSustainDelta;
      synthChannel.m_adr.m_stepADSR[3] = g_synth.m_instrument[i].m_asrReleaseDelta;

      synthChannel.m_adr.m_numADSR[0] =  g_synth.m_instrument[i].m_asrStepsAttack;
      synthChannel.m_adr.m_numADSR[1] =  0;
      synthChannel.m_adr.m_numADSR[2] =  g_synth.m_instrument[i].m_asrStepsSustain;
      synthChannel.m_adr.m_numADSR[3] =  g_synth.m_instrument[i].m_asrStepsRelease;

      synthChannel.m_lpf.cutoff =  g_synth.m_instrument[i].m_cutoff;
      synthChannel.m_lpf.resonance = g_synth.m_instrument[i].m_res;

    }
    else
    {
      error = 1;
    }

  }

  if (synthChannel.m_wave >= 0x01 && synthChannel.m_wave <= 0x1f)
  {
    // 711 = 22050 / 31 i.e. for half of sample rate when wave = 31 width = 22050 or 100%
     synthChannel.m_pulse_width = 711 * synthChannel.m_wave;
  }

 // unless the caller is going to commit this to the queue later.

    if (synthChannelPtr == NULL)
    {
      if ((synthChannel.m_mode & CHANNEL_MODE_QUEUE) == 0)
      {
        // printf("debug channel commit - %d\n", channel);
        ChannelCommit(&(g_synth.m_channel[channel]), &synthChannel);

      }
      else
      {
        QueueSound(channel, &synthChannel);
      }
   }
   else
   {
     memcpy(synthChannelPtr, &synthChannel, sizeof(SYNTH_CHANNEL));
   }


return error;
}

void HandleRFilter(_kernel_swi_regs *registers, SYNTH_CHANNEL *synthChannelPtr)
{
  extern RISC_SYNTH g_synth;
  int channel = (registers->r[0]-1) & MAX_POLY_MASK;

   if (synthChannelPtr)
   {
     synthChannelPtr->m_lpf.cutoff = registers->r[1];
     synthChannelPtr->m_lpf.resonance = registers->r[2];
   }
   else
   {
     g_synth.m_channel[channel].m_lpf.cutoff = registers->r[1];
     g_synth.m_channel[channel].m_lpf.resonance = registers->r[2];
   }
}

void HandleREnvelope(_kernel_swi_regs *registers)
{
  extern RISC_SYNTH g_synth;
  int entry = (registers->r[0] & 0xff);
  char *name = (char*)(registers->r[1]);
  int wave =  (registers->r[2] & 0x1ff);
  int cutoff = ((registers->r[3]) & 0xff00) >> 8;
  int resonance = ((registers->r[3]) & 0xff);
  int options = (registers->r[4] & 0xff00) >> 8;
  int steptime =  (registers->r[4] & 0xff);
  int asrStepsAttack = (registers->r[5] & 0xff0000) >> 16;
  int asrStepsSustain = (registers->r[5] & 0xff00) >> 8;
  int asrStepsRelease = (registers->r[5] & 0xff);
  int asrDeltaAttack = (registers->r[6] & 0xff0000) >> 16;
  int asrDeltaSustain = (registers->r[6] & 0xff00) >> 8;
  int asrDeltaRelease = (registers->r[6] & 0xff);
  int adsrDeltaAttack = (registers->r[7] & 0xff000000) >> 24;
  int adsrDeltaDecay = (registers->r[7] & 0x00ff0000) >> 16;
  int adsrDeltaSustain = (registers->r[7] & 0xff00) >> 8;
  int adsrDeltaRelease = (registers->r[7] & 0xff);
  INSTRUMENT_TABLE *instrument = (g_synth.m_instrument)+entry;

  if ((name != NULL) && (strlen(name) < 15))
  {
    strcpy(instrument->m_name, name);
  }

  instrument->m_wave = wave;
  instrument->m_options = options;
  instrument->m_steptime = steptime;
  instrument->m_cutoff = cutoff;
  instrument->m_res = resonance;
  instrument->m_asrStepsAttack = asrStepsAttack;
  instrument->m_asrStepsSustain = asrStepsSustain;
  instrument->m_asrStepsRelease = asrStepsRelease;
  instrument->m_asrAttackDelta =  ((signed char)asrDeltaAttack);
  instrument->m_asrSustainDelta = ((signed char)asrDeltaSustain);
  instrument->m_asrReleaseDelta = ((signed char)asrDeltaRelease);
  instrument->m_attack = adsrDeltaAttack;
  instrument->m_decay = adsrDeltaDecay;
  instrument->m_sustain = adsrDeltaSustain;
  instrument->m_release = adsrDeltaRelease;

}

void HandleRFX(_kernel_swi_regs *registers)
{
  if ((registers->r[0] >= 0) &&
      (registers->r[0] <= (8+16+32)))
  {
    extern RISC_SYNTH g_synth;
    g_synth.m_effects.m_mode = registers->r[0];
  }
}

void HandleRFXMix(_kernel_swi_regs *registers)
{
  if (((registers->r[0] >= 1) &&
       (registers->r[0] <= 16)) ||
       (registers->r[0] == 255))
  {
    extern RISC_SYNTH g_synth;
    if (registers->r[0] == 255)
    {
      int i;
      for (i = 0; i < 16; ++i)
      {
        g_synth.m_effectChannel[i] = 0;
      }
    }
    else
    {
      g_synth.m_effectChannel[registers->r[0]-1] = registers->r[1];
    }
  }
}

// To ensure that this does not affect existing software
// and for simplicity - panning will not increase the volume level
// on the panned channel i.e. only attenuate the volume on the
// channel panned away from
void HandleRPan(_kernel_swi_regs *registers, SYNTH_CHANNEL *synthChannelPtr)
{
  int channel = (registers->r[0]-1) & MAX_POLY_MASK;

  extern RISC_SYNTH g_synth;
  int pan = (int)registers->r[1];
  SYNTH_CHANNEL *synthChannelSelected = &(g_synth.m_channel[channel]);

  if (synthChannelPtr)
  {
    synthChannelSelected = synthChannelPtr;
  }

  synthChannelSelected->m_volumeLeft = 256;
  synthChannelSelected->m_volumeRight = 256;
  if (pan < 128)
  {
    synthChannelSelected->m_volumeRight -= (128-pan)*2;
  }
  else if (pan > 128)
  {
     synthChannelSelected->m_volumeLeft -= (pan-128)*2+15;
  }


}

void HandleRDelay(_kernel_swi_regs *registers)
{
    extern RISC_SYNTH g_synth;

    if ((registers->r[0] < 147) && (registers->r[1] < 256))
    {
      g_synth.m_effects.m_delayLine.m_record = registers->r[0] * 441;
      g_synth.m_effects.m_delayLine.m_play = 0;
      g_synth.m_effects.m_delayLine.m_feedback = registers->r[1];
    }
}

void HandleRChorus(_kernel_swi_regs *registers)
{
    extern RISC_SYNTH g_synth;

    if ((registers->r[0] < 16) && (registers->r[1] < 256))
    {
      g_synth.m_effects.m_chorus.m_lfoRate = registers->r[0];
                   // Oscillator (speed) increment where 262144 is 1
      g_synth.m_effects.m_chorus.m_lfoDepth = 262144 - ((32 * registers->r[1])
                                                       + 8160);

                   // depth is the polarity switch point
    }
}

void HandleRRoom(_kernel_swi_regs *registers)
{
   SetReverbRoom((unsigned char)(registers->r[0]));
}

void HandleRHall(_kernel_swi_regs *registers)
{
  SetReverbHall((unsigned char)(registers->r[0]));
}

#define RLOAD_ERROR_NOT_INIT 1
#define RLOAD_ERROR_NOT_FOUND 2
#define RLOAD_ERROR_BAD_FORMAT 3
#define RLOAD_ERROR_CHANNEL_BUSY 4
#define RLOAD_ERROR_MEMORY_ERROR 5
#define RLISTEN_ERROR_BUSY 6
#define RLISTEN_ERROR_OFF 7
#define RSAVE_ERROR_EMPTY 8
#define RSAVE_ERROR_FILE 9
#define RSAVE_ERROR_BUSY 10

int HandleRLoad(_kernel_swi_regs *registers)
{
  int error = 0;
  extern RISC_SYNTH g_synth;
  int sampleID = registers->r[0];
  char *wav_file = (char*)registers->r[1];
  int i;
  int found = 0;

  if (g_initialised == 0)
  {
    error = RLOAD_ERROR_NOT_INIT;
    return error;
  }

  // No channel must be playing when this function is called
  for (i = 0; i < 16; ++i)
  {
    if (g_synth.m_channel[i].m_adsr.m_stage !=  g_synth.m_channel[i].m_adsr.m_endState)
      found = 1;
  }
  if (found)
  {
    error = RLOAD_ERROR_CHANNEL_BUSY;
  }
  else
  {
    FILE *fptr;
    int length = 0;
    // Header for valid wave file RIFF<chunk size>Wavefmt,16 bit, mono,
    // 44khz
    unsigned char wave_header_44[] = {0x52,0x49,0x46,0x46,0x68,0x40,0x40,0x00,
                                       0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,
                                       0x12,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
                                       0x44,0xac,0x00,0x00,0x88,0x58,0x01,0x00,
                                       0x02,0x00,0x10,0x00,0x00,0x00,0x64,0x61,
                                       0x74,0x61};


    #define VALID_HEADER_SIZE 42

    unsigned char wave_header[VALID_HEADER_SIZE];

    int result = 1;

    // verify file & header
    if ((fptr = fopen(wav_file, "rb")) == NULL)
    {
       result = 0;
       error = RLOAD_ERROR_NOT_FOUND;
    }

    if (result == 1)
    {
		// read header (RIFF)
        result = 0;
        fread(wave_header, sizeof(unsigned char), 4, fptr);

        if ((memcmp(wave_header_44,
                                              wave_header,
                                              4) == 0))
        {
            result = 1;
        }
        else
        {
            error = RLOAD_ERROR_BAD_FORMAT;
        }

    }
    if (result == 1)
    {
        int i = 30;
		int founddata = 0;

        // Increased range of search from 50 to 500 for metadata skipping
		while ((i < 500) && (!founddata))
		{
    		char tmp_buffer[4] = {0};
			// try offset
			fseek(fptr, i, SEEK_SET);

			fread(tmp_buffer, 1, 4, fptr);

			if (strncmp(tmp_buffer, "data", 4) == 0)
			{
				founddata = 1;
			}

			i++;
		}

		if (founddata)
		{
	        // read header
		    fread(&length, sizeof(int), 1, fptr);
		}
		else
		{
			 result = 0;
        error = RLOAD_ERROR_BAD_FORMAT;
		}


    }


    // TODO If sample is not 44khz 16 bit then normalize on load
    //      i.e. convert to 16 bit 44khz (avoids complexity and processing
    //           later).

    if (result)
    {
      if (g_synth.m_sample[sampleID].m_data != NULL)
      {
        free(g_synth.m_sample[sampleID].m_data);
      }

      g_synth.m_sample[sampleID].m_data = (short*)malloc(length);
      g_synth.m_sample[sampleID].m_length = length / sizeof(short);
      if (g_synth.m_sample[sampleID].m_data == NULL)
      {
        error = RLOAD_ERROR_MEMORY_ERROR;
      }
      else
      {

        fread(g_synth.m_sample[sampleID].m_data, sizeof(short),
              g_synth.m_sample[sampleID].m_length, fptr);

        g_synth.m_sample[sampleID].m_loop = 0;
        g_synth.m_sample[sampleID].m_loopBack = 0;
      }
    }

    // For now just report that the length could be read
    if (fptr != NULL)
        fclose(fptr);
  }

  return error;
}

void HandleRList(void)
{
  extern RISC_SYNTH g_synth;
  int i;

  // List all envelopes
  printf("number,name,wave,options,steptime,cut-off,res,steps,delta,attack,decay,sustain ,release\n");
  for (i = 0; i < 256; ++i)
  {
    if (strcmp(g_synth.m_instrument[i].m_name, "") != 0)
    {
      printf("%d, %s,%d,%d,%d,%d,%d,%d %d %d,%d %d %d,%d,%d,%d,%d\n",
      i,
      g_synth.m_instrument[i].m_name,
      g_synth.m_instrument[i].m_wave,
      g_synth.m_instrument[i].m_options,
      g_synth.m_instrument[i].m_steptime,
      g_synth.m_instrument[i].m_cutoff,
      g_synth.m_instrument[i].m_res,
      g_synth.m_instrument[i].m_asrStepsAttack,
      g_synth.m_instrument[i].m_asrStepsSustain,
      g_synth.m_instrument[i].m_asrStepsRelease,
      g_synth.m_instrument[i].m_asrAttackDelta,
      g_synth.m_instrument[i].m_asrSustainDelta,
      g_synth.m_instrument[i].m_asrReleaseDelta,
      g_synth.m_instrument[i].m_attack,
      g_synth.m_instrument[i].m_decay,
      g_synth.m_instrument[i].m_sustain,
      g_synth.m_instrument[i].m_release);
    }
  }
}

void HandleRVolume(_kernel_swi_regs *registers)
{
  extern RISC_SYNTH g_synth;

  g_synth.m_masterVolume = registers->r[0];
}

void HandleRSampleLoop(_kernel_swi_regs *registers)
{
  extern RISC_SYNTH g_synth;
  int sampleID = registers->r[0];
  int loopPoint = registers->r[1];
  int loopTo = registers->r[2];

  if ((loopTo < loopPoint) && (loopTo > 0) && (loopPoint > 0))
  {
    if (loopPoint <= g_synth.m_sample[sampleID].m_length)
    {
        g_synth.m_sample[sampleID].m_loop = loopPoint;
        g_synth.m_sample[sampleID].m_loopBack = loopTo;
    }
  }
}

void HandleRBell(_kernel_swi_regs *registers)
{
  extern RISC_SYNTH g_synth;

  // To turn off set bell to 0
  if (g_synth.m_sample[registers->r[0]].m_length != 0)
    g_bell = registers->r[0];

  if (registers->r[0] == 0)
    g_bell = 0;


}

int HandleRListen(_kernel_swi_regs *registers)
{
  int error = 0;
  extern RISC_SYNTH g_synth;

  // RListen cannot be triggered if listening is active
  if (g_synth.m_recorder.m_on == 0)
  {
    // Check if buffer is large enough for requested time
    int requested = registers->r[0] * CENTISECOND;

    if (requested >= g_synth.m_recorder.m_size)
    {
      if (g_synth.m_recorder.m_left != NULL)
      {
        free(g_synth.m_recorder.m_left);
      }
      if (g_synth.m_recorder.m_right != NULL)
      {
        free(g_synth.m_recorder.m_right);
      }
      g_synth.m_recorder.m_left = (signed short*)malloc(sizeof(signed short) * requested);
      g_synth.m_recorder.m_right = (signed short*)malloc(sizeof(signed short) * requested);
      g_synth.m_recorder.m_size = requested;
    }

    g_synth.m_recorder.m_position = 0;
    g_synth.m_recorder.m_on = 1;

  }
  else
  {
    error = RLISTEN_ERROR_BUSY;
  }


  return error;
}

int HandleRNoListen(_kernel_swi_regs *registers)
{
  int error = 0;
  if (g_synth.m_recorder.m_on)
  {
    g_synth.m_recorder.m_on = 0;
  }
  else
  {
    error = RLISTEN_ERROR_OFF;
  }

  return error;
}

int HandleRSave(_kernel_swi_regs *registers)
{
  int error = 0;
  extern RISC_SYNTH g_synth;
  char *wav_file = (char*)registers->r[0];
  int i;

  if (g_synth.m_recorder.m_position != 0)
  {
    if ((g_synth.m_recorder.m_left != NULL) &&
        (g_synth.m_recorder.m_right != NULL))
    {
      // Write out header
      FILE *fptr = fopen(wav_file, "wb+");

      if (fptr)
      {
          char  ckID[4];             /* chunk id 'RIFF'            */
          DWORD ckSize;              /* chunk size                 */
          char  wave_ckID[4];        /* wave chunk id 'WAVE'       */
          char  fmt_ckID[4];         /* format chunk id 'fmt '     */
          DWORD fmt_ckSize;          /* format chunk size          */
          WORD  formatTag;           /* format tag currently pcm   */
          WORD  nChannels;           /* number of channels         */
          DWORD nSamplesPerSec;      /* sample rate in hz          */
          DWORD nAvgBytesPerSec;     /* average bytes per second   */
          WORD   nBlockAlign;        /* number of bytes per sample */
          DWORD  nBitsPerSample;     /* number of bits in a sample. Amended based on real data*/
          char  data_ckID[4];        /* data chunk id 'data'       */
          DWORD data_ckSize;         /* length of data chunk       */
          DWORD len;

          len = (DWORD)(g_synth.m_recorder.m_position) * 2;

          /* Write the wave header: Description: */

          strncpy(ckID, "RIFF",4);
          fwrite(ckID, 1, 4, fptr);

          ckSize = (len * sizeof(short)) + 36;
          fwrite(&ckSize, sizeof(DWORD), 1, fptr);

          strncpy(wave_ckID, "WAVE",4);
          fwrite(&wave_ckID, 1, 4, fptr);

          strncpy(fmt_ckID, "fmt ",4);
          fwrite(fmt_ckID, 1, 4, fptr);

          fmt_ckSize = 16;
          fwrite(&fmt_ckSize, sizeof(DWORD), 1, fptr);

          formatTag = 1;
          fwrite(&formatTag, sizeof(WORD), 1, fptr);

          nChannels = 2;
          fwrite(&nChannels, sizeof(WORD), 1, fptr);

          nSamplesPerSec = 44100;//44100;
          fwrite(&nSamplesPerSec, sizeof(DWORD), 1, fptr);

          nAvgBytesPerSec = 88200 * 2;
          fwrite(&nAvgBytesPerSec, sizeof(DWORD), 1, fptr);

          nBlockAlign = 4; // 2 channels - 2 bytes per channel
          fwrite(&nBlockAlign, sizeof(WORD), 1, fptr);

          nBitsPerSample = 16; // using shorts to generate hi-fi sound
          fwrite(&nBitsPerSample, sizeof(WORD), 1, fptr);

          strncpy(data_ckID, "data",4);
          fwrite(data_ckID, 1, 4, fptr);

          data_ckSize = len * sizeof(short); // length field
          fwrite(&data_ckSize, 1, 4, fptr);

          // Dump out - channels interleaved
          for (i = 0; i < (len / 2); ++i)
          {
            fwrite(g_synth.m_recorder.m_left+i, sizeof(short), 1, fptr);
            fwrite(g_synth.m_recorder.m_right+i, sizeof(short), 1, fptr);
          }

          fclose(fptr);
      }
      else
      {
        error = RSAVE_ERROR_FILE;
      }
    }
    else
    {
      error = RSAVE_ERROR_EMPTY;
    }
  }
  else
  {
    error = RSAVE_ERROR_BUSY;
  }

  return error;
}

// End SWI implementation


typedef enum
{
  RSTART = 0,
  RSTOP = 1,
  RFILTER = 2,
  RSOUND = 3,
  RENVELOPE = 4,
  RFXMIX = 5,
  RFX = 6,
  RDELAY = 7,
  RLOAD = 8,
  RVERBOSE = 9,
  RLEGACY = 10,
  RADVANCED = 11,
  RLIST = 12,
  RVOLUME = 13,
  RLISTEN = 14,
  RNOLISTEN = 15,
  RSAVE = 16,
  RCOMPRESSOR = 17,
  RSAMPLELOOP = 18,
  RBELL = 19,
  RPAN = 20,
  RDRIVE = 21,
  RCHORUS = 22,
  RHALL = 24,
  RROOM = 25,
  RUNKNOWN = 65
} STAR_COMMAND;

_kernel_oserror *rs_swi(int swi_no, _kernel_swi_regs *r, void *private_word)
{
  UNUSED(private_word);

  #define SWI_BASE 0x59B00
  switch (swi_no)
  {
    case (SWI_BASE + RSTART):
      HandleRStart();
      break;
    case (SWI_BASE + RSTOP):
      HandleRStop();
      break;
    case (SWI_BASE + RFILTER):
      HandleRFilter(r, NULL);
      break;
    case (SWI_BASE + RSOUND):
      HandleRSound(r, NULL);
      break;
    case (SWI_BASE + RENVELOPE):
      HandleREnvelope(r);
      break;
    case (SWI_BASE + RFXMIX):
      HandleRFXMix(r);
      break;
    case (SWI_BASE + RFX):
      HandleRFX(r);
      break;
    case (SWI_BASE + RDELAY):
      HandleRDelay(r);
      break;
    case (SWI_BASE + RLOAD):
      HandleRLoad(r);
      break;
    case (SWI_BASE + RVERBOSE):
      g_verbose = 1;
      break;
    case (SWI_BASE + RLEGACY):
      break;
    case (SWI_BASE + RADVANCED):
      break;
    case (SWI_BASE + RLIST):
      break;
    case (SWI_BASE + RVOLUME):
      HandleRVolume(r);
      break;
    case (SWI_BASE + RLISTEN):
      HandleRListen(r);
      break;
    case (SWI_BASE + RNOLISTEN):
      HandleRNoListen(r);
      break;
    case (SWI_BASE + RSAVE):
      HandleRSave(r);
      break;
    case (SWI_BASE + RCOMPRESSOR):
      break;
    case (SWI_BASE + RPAN):
      HandleRPan(r, NULL);
      break;
    case (SWI_BASE + RDRIVE):
      break;
    case (SWI_BASE + RCHORUS):
      HandleRChorus(r);
      break;
    case (SWI_BASE + RHALL):
      HandleRHall(r);
      break;
    case (SWI_BASE + RROOM):
      HandleRRoom(r);
      break;
    default:
      break;

  };

  return 0;
}

typedef enum
{
  ERROR_NONE,
  ERROR_UNKNOWN_COMMAND,
  ERROR_MISSING_PARAMETER,
  ERROR_OUT_OF_RANGE
} COMMAND_ERROR;

// Command format table
typedef enum
{
  ET_STRING,
  ET_INTEGER,
  ET_HEX,
  ET_END
} ELEMENT_TYPE;

typedef struct
{
  ELEMENT_TYPE m_type;
  int          m_minRange;
  int          m_maxRange;
} COMMAND_PARAM;

#define MAX_COMMAND 256
#define MAX_PARAM   8
typedef struct
{
  char *m_name;
  STAR_COMMAND m_cmdid;
  COMMAND_PARAM m_param[9];
} COMMAND_STRUCT;

#define _EMPTY {ET_END, 0, 0}
#define _EMPTY_8 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY
#define _EMPTY_2 _EMPTY, _EMPTY
#define _EMPTY_5 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY
#define _EMPTY_6 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY
#define _EMPTY_7 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY

COMMAND_STRUCT m_commands[] = {
  { "*rstart", RSTART, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rstop", RSTOP, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rfilter", RFILTER, {{ET_INTEGER, 1, 16}, {ET_INTEGER , 0, 255}, {ET_INTEGER, 0, 12}, _EMPTY_5}},
  { "*rsound", RSOUND, {{ET_INTEGER, 1, 16}, {ET_INTEGER, 0, 2}, {ET_STRING, 0, 0}, {ET_INTEGER, 0, 255 }, {ET_INTEGER, 0, 364}, {ET_INTEGER, 0, 0xffffff}, _EMPTY_2}},
  //                           Env No                 Name        wave       Cutoff-res
  { "*renvelope", RENVELOPE, {{ET_HEX, 0, 255}, {ET_STRING, 0,0}, {ET_HEX, 0, 0xff},  {ET_HEX, 0, 0xffff},
  //  options,ASR steptime       steptime          ASR steps
  {ET_HEX, 0, 0xffffff}, {ET_HEX, 0, 0xffffff},
  // ASR delta                ADSR
  {ET_HEX, 0, 0xffffff}, {ET_HEX, 0, 0}, _EMPTY}},
  { "*rfxmix", RFXMIX, {{ET_INTEGER, 1, 255}, {ET_INTEGER, 0, 1}, _EMPTY_7}},
  { "*rfx", RFX, {{ET_INTEGER, 1, 56}, _EMPTY_8}},
  { "*rdelay", RDELAY, {{ET_INTEGER, 0, 255}, {ET_INTEGER, 0, 255}, _EMPTY_7}},
  { "*rload", RLOAD, {{ET_INTEGER, 0,255}, {ET_STRING, 0,0}, _EMPTY_7}},
  { "*rverbose", RVERBOSE, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rlegacy", RLEGACY, {{ET_END, 0,0}, _EMPTY_8}},
  { "*radvanced", RADVANCED, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rlist", RLIST, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rvolume", RVOLUME, {{ET_INTEGER, 0, 255}, _EMPTY_8}},
  { "*rlisten", RLISTEN, {{ET_INTEGER, 1, 120000}, _EMPTY_8}},
  { "*rnolisten", RNOLISTEN, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rsave", RSAVE,  {{ET_STRING, 0, 0}, _EMPTY_8}},
  { "*rcompressor", RCOMPRESSOR, {{ET_INTEGER, 16, 256}, _EMPTY_8}},
  { "*rsampleloop", RSAMPLELOOP, {{ET_INTEGER, 0, 255}, {ET_INTEGER, 0,0xfffffff}, {ET_INTEGER, 0, 0xfffffff}, _EMPTY_6}},
  { "*rbell", RBELL, {{ET_INTEGER, 0, 255}, _EMPTY_8}},
  // Not implemented yet.

  { "*rpan", RPAN, {{ET_INTEGER, 1, 16}, {ET_INTEGER, 0, 255}, _EMPTY_7}},
  // Bug : These two are around the wrong way
  { "*rchorus", RCHORUS, {{ET_INTEGER, 1, 16}, {ET_INTEGER, 0, 255}, _EMPTY_7}},
//  { "*rdrive", RDRIVE, {{ET_INTEGER, 0, 255}, {ET_INTEGER, 0, 64}, _EMPTY_7}},
  { "*rhall", RHALL, {{ET_INTEGER, 0, 255}, _EMPTY_8}},
  { "*rroom", RROOM, {{ET_INTEGER, 0, 255}, _EMPTY_8}},
  { NULL, RUNKNOWN, {{ET_END, 0,0}, _EMPTY_8}}
};

COMMAND_ERROR GetCommand(int cmd_no, const char *arg_list,
                STAR_COMMAND *command,
                _kernel_swi_regs *regs)
{
  int i = 0;
  int found = 0;
  COMMAND_ERROR err = ERROR_UNKNOWN_COMMAND;
  char cmd_buffer[4096];
  char *cmdToken = cmd_buffer;

  // TODO size checks etc.
  cmd_buffer[0] = '\0';
  strcpy(cmd_buffer+1, arg_list);

   int j = 0;
   i = cmd_no;
   *command = m_commands[i].m_cmdid;
   err = ERROR_NONE;
   found = 1;
   while ((m_commands[i].m_param[j].m_type != ET_END) &&
          (err == ERROR_NONE) && j < 9)
   {
     switch (m_commands[i].m_param[j].m_type)
     {
         case ET_STRING:
             // workaround possibly strtok in GCC is broken
             // Will revise when I move to DDE
           cmdToken = strtok(cmdToken+strlen(cmdToken)+1, " \t\n");

           if (cmdToken == NULL)
             err = ERROR_MISSING_PARAMETER;
           else
             regs->r[j] =(int)cmdToken;
             break;
         case ET_INTEGER:
           cmdToken = strtok(cmdToken+strlen(cmdToken)+1, " \t\n");

           if (cmdToken == NULL)
             err = ERROR_MISSING_PARAMETER;
           else
           {
             int value = atoi(cmdToken);
             if (((value < m_commands[i].m_param[j].m_minRange) ||
                  (value > m_commands[i].m_param[j].m_maxRange)) &&
                  (m_commands[i].m_param[j].m_maxRange !=
                   m_commands[i].m_param[j].m_minRange))
             {
               err = ERROR_OUT_OF_RANGE;
             }
             else
               regs->r[j] =value;
           }
           break;
         case ET_HEX:
           cmdToken = strtok(cmdToken+strlen(cmdToken)+1, " \t\n");

           if (cmdToken == NULL)
             err = ERROR_MISSING_PARAMETER;
           else
           {
             int value = 0;
             sscanf(cmdToken, "%x", &value);

             if (((value < m_commands[i].m_param[j].m_minRange) ||
                  (value > m_commands[i].m_param[j].m_maxRange)) &&
                  (m_commands[i].m_param[j].m_maxRange !=
                   m_commands[i].m_param[j].m_minRange))
             {
               err = ERROR_OUT_OF_RANGE;
             }
             else
               regs->r[j] =value;
           }
           break;
         default:
       //  printf("default %d\n, ", m_commands[i].m_param[j].m_type);
           break;
        }
        ++j;
      }


  return err;
}


int snd = 0;
long store[4] = {0};

_kernel_oserror *rs_cmd(const char *arg_string, int argc, int cmd_no, void *pw)
{
    COMMAND_ERROR error = ERROR_UNKNOWN_COMMAND;
    STAR_COMMAND command;
    _kernel_swi_regs regs;
    static _kernel_oserror cmd_error = { 0x88000, "" };

    UNUSED(pw);

    error = GetCommand(cmd_no, arg_string, &command, &regs);
    cmd_error.errnum = 0;
    strcpy(cmd_error.errmess, "");
    switch (error)
    {
       case ERROR_NONE:
        break;
      case ERROR_UNKNOWN_COMMAND:
        cmd_error.errnum = 0x59b00;
        strcpy(cmd_error.errmess, "Command not implemented\n");
        break;
      case ERROR_MISSING_PARAMETER:
        cmd_error.errnum = 0x59b00;
        strcpy(cmd_error.errmess, "Missing Parameter\n");
        break;
      case ERROR_OUT_OF_RANGE:
        cmd_error.errnum = 0x59b00;
        strcpy(cmd_error.errmess, "Parameter value out of range\n");
        break;
      default:
        break;

    }


    {
    //DEBUG code
    // extern RISC_SYNTH g_synth;
    //  printf("debug = %d\n", g_synth.debug);
    // printf("pos = %d, size = %d, on = %d\n", g_synth.m_recorder.m_position,
    //                                          g_synth.m_recorder.m_size,
    //                                          g_synth.m_recorder.m_on);
    }
    // Next can call SWI implementation
    switch (command)
    {
      case RSTART:
        HandleRStart();
        break;
      case RSTOP:
        HandleRStop();
        break;
      case RSOUND:
        HandleRSound(&regs, NULL);
        break;
      case RFILTER:
        HandleRFilter(&regs, NULL);
        break;
      case RENVELOPE:
        HandleREnvelope(&regs);
        break;
      case RFX:
        HandleRFX(&regs);
        break;
      case RFXMIX:
        HandleRFXMix(&regs);
        break;
      case RPAN:
        HandleRPan(&regs, NULL);
        break;
      case RDELAY:
        HandleRDelay(&regs);
        break;
      case RCHORUS:
        HandleRChorus(&regs);
        break;
      case RLOAD:
      {
        int error =  HandleRLoad(&regs);

        switch (error)
        {
          case RLOAD_ERROR_NOT_INIT:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "RDSP has not been initialised.\n");
            break;
          case RLOAD_ERROR_NOT_FOUND:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "Sample file not found.\n");
            break;
          case RLOAD_ERROR_BAD_FORMAT:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "Sample file has unrecognised format. Expected 44khz 16 bit WAV file\n");
            break;
          case RLOAD_ERROR_CHANNEL_BUSY:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "Samples will not be loaded whilst RDSP is playing a sound.\n");
            break;
        }

        break;
      }
      case RVERBOSE:
        g_verbose = 1;
        break;
      case RLEGACY:
        cmd_error.errnum = 0x59b00;
        strcpy(cmd_error.errmess, "Legacy mode is not support in this RDSP release.\n");
        break;
      case RADVANCED:
        g_legacy = 0;
        break;
      case RLIST:
        HandleRList();
        break;
      case RVOLUME:
        HandleRVolume(&regs);
        break;
      case RSAMPLELOOP:
        HandleRSampleLoop(&regs);
        break;
      case RBELL:
        HandleRBell(&regs);
        break;
      case RLISTEN:
      {
        int error = HandleRListen(&regs);

        switch (error)
        {
        case RLISTEN_ERROR_BUSY:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "Recording will not be started whilst RDSP is already recording.\n");
            break;
        }
        break;
      }
      case RNOLISTEN:
      {
        int error = HandleRNoListen(&regs);

        switch (error)
        {
        case RLISTEN_ERROR_OFF:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "A recording is not in progress. It may have finished or already been stopped..\n");
            break;
        }
        break;
      }
      case RSAVE:
      {
        int error = HandleRSave(&regs);

        switch (error)
        {
          case RSAVE_ERROR_FILE:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "Failed to create file.\n");
            break;
          case RSAVE_ERROR_EMPTY:
            cmd_error.errnum = 0x59b00;
            strcpy(cmd_error.errmess, "Recorder buffer is empty. Nothing to save.\n");
            break;
        }


        break;
      }
      case RHALL:
        HandleRHall(&regs);
        break;
      case RROOM:
        HandleRRoom(&regs);
        break;
      default:
        cmd_error.errnum = 0x59b00;
        strcpy(cmd_error.errmess, "Command not implemented\n");
        break;

    }

    if (cmd_error.errnum)
    {
      return &cmd_error;
    }
    return 0;
}

void rs_service(int service_number, _kernel_swi_regs *regs, void *private_word)
{
    UNUSED(private_word);
/*
 *  In general, it is a Bad Thing to use printf inside an arbitrary
 *  service call handler (e.g. in one that catches errors - call 6 -
 *  as this one does. Therefore we merely count what's happening and
 * print it out when the TestCModule's SWI is called.
 */
    if (service_number == UNKNOWN_CMD_SERVICE)
        ++unknown_cmd_count;
    else
      ++other_service_call_count;

    if (regs->r[1] == 8)
    {
      if (regs->r[2] == 8)
      {
        snd++;
        store[0] = ((unsigned char*)regs->r[3])[0];
        store[1] = ((unsigned char*)regs->r[3])[1];
        store[2] = ((unsigned char*)regs->r[3])[2];
      }
    }
}

_kernel_oserror *rs_initialise(const char *cmd_tail, int podule_base, void *private_word)
{
    _kernel_swi_regs regs;
    UNUSED(cmd_tail);
    UNUSED(podule_base);
    //    UNUSED(private_word);

 // Now claim OS WORD 7. Vector is 7 for OS WORD itself
	regs.r[0] = 7;
	regs.r[1] = (int)&rosesound_entry;
	regs.r[2] = (int)private_word;
 	_kernel_swi(OS_Claim,&regs,&regs);

    unknown_cmd_count = other_service_call_count = 0;
    return NULL;
}

_kernel_oserror *rs_finalise(int fatal, int podule_base, void *private_word)
{
    _kernel_swi_regs regs;
    UNUSED(fatal);
    UNUSED(podule_base);
    //    UNUSED(private_word);

 // Now claim OS WORD 7. Vector is 7 for OS WORD itself
	regs.r[0] = 7;
	regs.r[1] = (int)&rosesound_entry;
	regs.r[2] = (int)private_word;
 	_kernel_swi(OS_Release,&regs,&regs);

    // Unloading force refcount to 0
    g_verbose = 0;
    rstart_refcount = 0;
    HandleRStop();

    unknown_cmd_count = other_service_call_count = 0;
    return NULL;
}

// Handler for OS_WORD 7 & 8
int rosesound_handler( _kernel_swi_regs *r, void *pw)
{


  if (r->r[0] == 7)
  {
      SYNTH_CHANNEL synthChannel;
      SYNTH_CHANNEL *synthChannelPtr = NULL;
      int frequency = 0;
      int amplitude = 0;
      int wave = 0;
      int bellon = 0;
      _kernel_swi_regs regs;
      char wavestr[255];
      unsigned char *paramBlock = (unsigned char*)(r->r[1]);
      int channel = ((paramBlock[0] & 0x0f)-1) & MAX_POLY_MASK;
      int mode = (paramBlock[1] & 0x0f) << 4;

      memset(&synthChannel, 0, sizeof(SYNTH_CHANNEL));

      wavestr[0] = 0;
      // Raise this on a forum thread for discussion on ROOL tomorrow.

      // LSB MSB - sound uses 16 bit values
      // As per Rick's post
      // Make semi-backwards compatible
      // channel flags wave/instrument volume pitch duration


      //
      // pitch can go through all values - If you Start at &0100 you get full
      // range
      // Duration is up to 65536
      //
      // Samples can be indexed by negative pitches
      // So the wave parameter becomes the sample number selector
      // i.e. SOUND &21, &00FF, 100, 100
      // Plays sample 0 at pitch 1 for 1 second
      // To play instrument 1 on channel 1 I can split the last byte
      // So volume can be 16 value logarithmic for negative numbers
      // If TT = 255 Then treat this as square wave for legacy VDU 7 etc
      // SOUND <PFSC>,<TTVV>, <IRNN>, <Duration>
      // P = Pan
      // F = flags &10 = Sync, &20 = mute, &40 = xor
      // S = sound &00 = wave &10 = Envelop, &20 = Sample,
      // I = cutoff (if zero then this is open as optional) bits 16,15,14,13
      // R = resonance bits (12,11,10)
      // N = note bits 1 to 9

      //   n.b. This allows The sample to be used for an instrument
      //        i.e. The sample will loop over the ADSR.
      // C = Channel &01 to &08
      // TT = Tone = Wave, Sample number or instrument
      // VV = volume
      //
      // n.b. revise *RLOAD to <filename> <instrument name> <loop point> <slot>
      // *RLEGACY will set pitch range to start at BBC Micro range
      //          and map envelope parameters to legacy parameters.

    amplitude = paramBlock[2];

    if (paramBlock[0] & 0x20)
    {
      wave = (((int)paramBlock[3]) + 256) & 0x1ff;
      sprintf(wavestr, "%d", wave);
    }
    else
    if (paramBlock[0] & 0x10)
    {
      extern RISC_SYNTH g_synth;

      strcpy(wavestr, g_synth.m_instrument[paramBlock[3]&0xff].m_name);
    }
    else
    {
       // If SOUND channel,-15
       // default to square wave
      if (paramBlock[3] == 0xff)
      {
        // Unless bell override is active
        if (g_bell && (paramBlock[0] == 1) && (paramBlock[4] == 100))
        {
          sprintf(wavestr, "%d", g_bell+0x100);
          bellon = 1;
        }
        else
          strcpy(wavestr, "31");

      }
      // If wave field is set to zero use amplitude field as envelope selector
      // and set amplitude to 0xff
      else if ((paramBlock[3] == 0x00) && (paramBlock[2] > 0))
      {
        strcpy(wavestr, g_synth.m_instrument[paramBlock[2]&0xff].m_name);
        amplitude = 0xff;
      }
      else
         sprintf(wavestr, "%d", paramBlock[3]);



       if ((paramBlock[3] == 0xff) && (paramBlock[2] > 0xf0))
       {
         amplitude = 255 - (((paramBlock[2] & 0x0f)-1) * 0x10);
       }
    }

    // Channel 0 through the SOUND command will map to noise.
    // and choose a matching noise
    // Should only be true if paramBlock[3] = 0xff i.e.
    //       legacy 4 bit amplitude selected
    if (((paramBlock[0] & 0x0f) == 0) && (paramBlock[3] == 0xff))
    {
      int table[] = {212, 164,116,0};

      if ((paramBlock[4] & 0x7) < 4)
      {
        strcpy(wavestr, "8");

      }
      else
      {
        strcpy(wavestr, "128");
      }
      if ((paramBlock[4] & 3) < 3)
      {
        frequency = table[paramBlock[4] & 3];
      }
      else
      {
        // Sync to channel 1
        frequency =g_synth.m_channel[0].m_tone;
      }
    }
    else
    {
      frequency = ((int)paramBlock[4]) | (((int)(paramBlock[5]&1)) << 8);
    }

    regs.r[0] = (paramBlock[0] & 0x0f);
    regs.r[1] = (paramBlock[1] & 0x0f) << 4;
    regs.r[2] = (int)wavestr;
    regs.r[3] = amplitude;
    // TODO: Consider mapping 255 to 2^31 (i.e. 68 years)
    // TODO: How do I get a module to respond to <interrupt>
    // As per BBC sound command - duration is in 20ths of a second.
    if (bellon)
    {
      regs.r[4] = 208;
      regs.r[5] = g_synth.m_sample[g_bell].m_length / 441;
    }
    else
    {
      regs.r[4] = frequency;
      regs.r[5] = ((int)paramBlock[6]) | (((int)paramBlock[7]) << 8) * 5;
    }

    synthChannelPtr = &synthChannel;

    {
      // Not sure if linear is better.
      int cutoffvalue[] = {255,2,3,4,6,8,11,16,23,32,45,64,90,128,181,255};
      int resvalue[] = {0,2,4,6,7,8,10,12};
      int cutoff = cutoffvalue[(paramBlock[5]&0xf0)>>4];
      int res = resvalue[(paramBlock[5]&0x0e) >> 1];

      _kernel_swi_regs filterParam;

      filterParam.r[0] = paramBlock[0] & 0x0f;
      filterParam.r[1] = cutoff;
      filterParam.r[2] = res;

      HandleRFilter(&filterParam, synthChannelPtr);

    }

    if (strcmp(wavestr, "") != 0)
      HandleRSound(&regs, synthChannelPtr);

    // Now pan channel
    regs.r[1] = (paramBlock[1] & 0xf0) >> 4;

    if (regs.r[1] == 0)
      regs.r[1] = 128;
    else
      regs.r[1] *= 16;

      HandleRPan(&regs, synthChannelPtr);

    // If queue flag is set - then commit all items into the queue in a
    // single call
    // The queue flag has a more limited behaviour via the RSound native
    // API.
    if (mode & CHANNEL_MODE_QUEUE)
    {
      QueueSound(channel, synthChannelPtr);
    }
    else
    {
      ChannelCommit(&(g_synth.m_channel[channel]), synthChannelPtr);
    }

    return 0;
    }

    // OS Word 8 - for the new envelope model and not
    //             for the legacy mode option
    //             <env-number 7 bit. bit 8=0 wave, 1 sample> <wave>
    //             <cutoff |resonance> <options)
    //        <ASR steptime>
    //        <ASR steps section 2>
    //        <ASR steps section 1 & 3>
    //        <ASR delta-attack>
    //        <ASR delta-sustain>
    //        <ASR delta-release>
    //               <Attack> <Decay> <sustain> <release>
    // To fit this in the BASIC command I will trade off some flexibility
    // cut off resonance can be reduced to 4 bit values
    //

    // Just realised. I could make the ADSR proportional to the sound call
    // So ADSR are fractions of total (A+D+S+R) and the duration parameter will
    // do something.
    if (r->r[0] == 8)
    {
 //       if (g_legacy == 0)
        {
          _kernel_swi_regs regs;
          unsigned char *paramBlock = (unsigned char*)(r->r[1]);

          char name[255];
          // User just needs to know this is a range of min to max
          // 1.414 ^ n
          int cutoffvalue[] = {1,2,3,4,6,8,11,16,23,32,45,64,90,128,181,255};

          // Could consider an offset value somewhere?
          //

          int cutoff, resonance;
          int options, steptime;
          int attack, decay, sustain, release;
          int adr1step, adr2step, adr3step;
          int adr1delta, adr2delta, adr3delta;
          //for (i = 0; i < 14; ++i)
          //printf("param block = %d = %d", i, paramBlock[i]);
          //printf("\n");

          sprintf(name, "envelope%d", paramBlock[0]);

          cutoff = cutoffvalue[((paramBlock[2] & 0xf0) >> 4)];
          resonance = paramBlock[2] & 0x0f;

          options = paramBlock[3];
          steptime = paramBlock[4];

          adr1step = paramBlock[5];
          adr2step = paramBlock[6];
          adr3step = paramBlock[5];
          adr1delta = paramBlock[7];
          adr2delta = paramBlock[8];
          adr3delta = paramBlock[9];

          attack = paramBlock[10];
          decay = paramBlock[11];
          sustain = paramBlock[12];
          release = paramBlock[13];

          regs.r[0] = (paramBlock[0] & 0x7F);
          regs.r[1] = (int)name;
          regs.r[2] = (int)(paramBlock[1]) + ((((int)paramBlock[0]) & 0x80)<<1) ;

          regs.r[3] = (cutoff << 8) | resonance;
          regs.r[4] = ((int)(options << 8)) | (int)steptime;
          regs.r[5] = (adr1step << 16) | (adr2step << 8) | adr1step;
          regs.r[6] = (adr1delta << 16) | (adr2delta << 8) | adr3delta;
          regs.r[7] = (attack << 24) | (decay << 16) | (sustain << 8) | release;
          HandleREnvelope(&regs);
        }
//        else
  /*      {
          // I need to re-interpret further - so do not deliver legacy
          // mode in 1st release of alpha.
          //
          extern RISC_SYNTH g_synth;
          unsigned char *paramBlock = (unsigned char*)(r->r[1]);
          INSTRUMENT_TABLE *instrument = (g_synth.m_instrument)+paramBlock[0];
          int i;
          char name[255];

          int steptime;
          int options = 0;
          int attack, decay, sustain, release;
          int adr1step, adr2step, adr3step;
          int adr1delta, adr2delta, adr3delta;

          sprintf(name, "envelope%d", paramBlock[0]);

          if (paramBlock[1] & 128)
            options = 0;
          else
            options = 0x80;

          // Legacy mode locks the 3 stage envelope to pitch
          options += 0x10;

          steptime = paramBlock[1] & 0x7f;

          if ((name != NULL) && (strlen(name) < 15))
          {
            strcpy(instrument->m_name, name);
          }

          instrument->m_wave = 0x1f;
          instrument->m_options = options;
          instrument->m_steptime = steptime;
          instrument->m_cutoff = 255;
          instrument->m_res = 0;

          adr1step = paramBlock[5];
          adr2step = paramBlock[6];
          adr3step = paramBlock[7];
          adr1delta = paramBlock[2];
          adr2delta = paramBlock[3];
          adr3delta = paramBlock[4];

          instrument->m_asrStepsAttack = paramBlock[5];
          instrument->m_asrStepsSustain = paramBlock[6];
          instrument->m_asrStepsRelease = paramBlock[7];
          instrument->m_asrAttackDelta =  ((signed char)paramBlock[2]);
          instrument->m_asrSustainDelta = ((signed char)paramBlock[3]);
          instrument->m_asrReleaseDelta = ((signed char)paramBlock[4]);
          instrument->m_attack  = (unsigned char)paramBlock[12];
          instrument->m_decay   = (unsigned char)paramBlock[13];
          instrument->m_sustain = 0x80;
          instrument->m_release = ((signed char)paramBlock[11]);

        }*/
    }
  return 1;
}



