/***************************************************************************
*        NAME:  IWLFO.C $Revision: 1.7 $
**       COPYRIGHT:
**       "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: iwlfo.c $
* Revision 1.7  1995/10/13 17:25:14  mleibow
* Moved iwl_lfo_struct into iwl.h so that effects engine could use the LFO code.
* Revision 1.6  1995/09/28 15:52:31  wconway
* Changed reference to "iwlmacros.h" to "iwlmacro.h"
* Revision 1.5  1995/05/07 16:48:52  teckert
* Revision 1.4  1995/05/03 09:39:00  mleibow
* Fixed constant out of range warnings
* Revision 1.3  1995/05/03 08:28:12  sdsmith
* Added typecasts for addressing
* Revision 1.2  1995/04/21 19:14:55  mleibow
* Modified LFO code to be ignored if no memory on sound card
* Revision 1.1  1995/02/23 11:07:10  unknown
* Initial revision
***************************************************************************/
#include <stdlib.h>
#include "iw.h"
#include "iwl.h"
#include "globals.h"
#include "iwlmacro.h"

#define IWL_LFO_ENABLE      BIT15
#define IWL_LFO_WAVE_SELECT BIT14
#define IWL_LFO_SHIFT       BIT13
#define IWL_LFO_INVERT      BIT12
#define LFO_BLOCK_SIZE 16

#define LFO_BLOCK_ADDR(y,z) (iwl_lfo_base | ((ULONG)y << 5) | ((ULONG)z << 4))

iwl_lfo_struct lfo_parms[IW_MAX_VOICES];
unsigned long iwl_lfo_base = (unsigned long)-1;

int iwl_lfo_init(void)
{
    /* allocate an LFO memory block */
    iwl_lfo_base = iwl_steal_lfo_memory();
    if (iwl_lfo_base != (ULONG)-1) {
	/* set the LFO base address register */
	IWL_OUT_W(SET_LFO_BASE, (unsigned short)(iwl_lfo_base >> 10));
	
	/* set the LFO memory block to zeros */
	IWL_OUT_W(SET_DRAM_LOW, (unsigned short)iwl_lfo_base);
	IWL_OUT_B(SET_DRAM_HIGH, (unsigned char)((iwl_lfo_base >> 16)&0xFF));
	os_outset(iwl_dram_io, 0x00, 1024);
	
	/* Enable all LFOs */
	iwl_sgmi |= IWL_SGMI_GLFOE;
	IWL_OUT_B(SET_GLOBAL_MODE, iwl_sgmi);
    }
    return(IW_OK);
}

void iwl_lfo_write_block(
  int voice,
  unsigned char lfo_type,    /* IW_LFO_VIBRATO | IW_LFO_TRMELO */
  iwl_lfo_struct RFAR *lfo_block)
{
   unsigned long lfo_block_addr;

   if (iwl_lfo_base != (unsigned long)-1) {
       lfo_block_addr = iwl_lfo_base | ((ULONG)voice << 5) | ((ULONG)lfo_type << 4);
       ENTER;
       IWL_OUT_W(SET_DRAM_LOW, (USHORT)lfo_block_addr);
       IWL_OUT_B(SET_DRAM_HIGH, (UCHAR)((lfo_block_addr >> 16)&0xFF));
    //   OS_OUTPORTB(iwl_register_select, IW_LMSBAI);
       os_outs(/*iwl_data_low, */ (UCHAR RFAR *)lfo_block, sizeof(iwl_lfo_struct));
       LEAVE;
   }
} 

void iwl_lfo_enable(
  int voice,
  unsigned char lfo_type)    /* IW_LFO_VIBRATO | IW_LFO_TRMELO */
{
    unsigned long lfo_block_addr;
    unsigned short control;

    if (iwl_lfo_base != (unsigned long)-1) {
	ENTER;
	lfo_block_addr = LFO_BLOCK_ADDR(voice, lfo_type);
	control = iw_peek16(lfo_block_addr) | IWL_LFO_ENABLE;
	iw_poke16(lfo_block_addr, control);
	LEAVE;
    }
}

void iwl_lfo_disable(
  int voice,
  unsigned char lfo_type)    /* IW_LFO_VIBRATO | IW_LFO_TRMELO */
{
    unsigned long lfo_block_addr;
    unsigned short control;

    if (iwl_lfo_base != (unsigned long)-1) {
	ENTER;
	lfo_block_addr = LFO_BLOCK_ADDR(voice, lfo_type);
	control = iw_peek16(lfo_block_addr) & ~IWL_LFO_ENABLE;
	iw_poke16(lfo_block_addr, control);
	LEAVE;
    }
}

void iwl_lfo_change_freq(
  int voice,
  unsigned char  lfo_type,    /* IW_LFO_VIBRATO | IW_LFO_TRMELO */
  unsigned short frequency)
{
    unsigned long lfo_block_addr;
    unsigned short control;

    if (iwl_lfo_base != (unsigned long)-1) {
	ENTER;
	lfo_block_addr = LFO_BLOCK_ADDR(voice, lfo_type);
	control = (unsigned short)(iw_peek16(lfo_block_addr) & ~0x7ffffUL | (unsigned long)frequency);
	iw_poke16(lfo_block_addr, control);
	LEAVE;
    }
}

void iwl_lfo_change_depth(
  int voice,
  unsigned char lfo_type,    /* IW_LFO_VIBRATO | IW_LFO_TRMELO */
  short depth) /* 0x00 - 0xFF */
{
    unsigned long lfo_block_addr;
    unsigned short control;

    if (iwl_lfo_base != (unsigned long)-1) {
	ENTER;
	lfo_block_addr = LFO_BLOCK_ADDR(voice, lfo_type);
	control = iw_peek16(lfo_block_addr);
	if (depth < 0) {
	   control |= IWL_LFO_INVERT;
	   depth = -depth;
	} else {
	   control &= ~IWL_LFO_INVERT;
	}
	iw_poke16(lfo_block_addr, control);
	iw_poke(lfo_block_addr+2, (UCHAR)depth);
	depth <<= 5;
	iw_poke16(lfo_block_addr+10, depth);
	LEAVE;
    }
}

void iwl_lfo_setup(
  int voice,
  unsigned char lfo_type,
  unsigned short freq,
  unsigned short current_depth,
  short final_depth,
  short sweep,
  unsigned char shape)
{
    iwl_lfo_struct lfo_struct;

    if (iwl_lfo_base != (unsigned long)-1) {
	ENTER;
	iwu_memset(&lfo_struct, 0, sizeof(lfo_struct));
    
	lfo_struct.control = IWL_LFO_ENABLE;
	if (shape & IW_LFO_POSTRIANGLE) lfo_struct.control |= IWL_LFO_SHIFT;
	if (final_depth < 0) {
	   lfo_struct.control |= IWL_LFO_INVERT;
	   final_depth = -final_depth;
	}
	lfo_struct.control |= (freq & 0x07FF);
	lfo_struct.twave_0 = 0;
	lfo_struct.depth_0 = current_depth;
	lfo_struct.final_depth = (UCHAR)final_depth;
	if (sweep) {
	    lfo_struct.depth_inc = (UCHAR)((((long)((final_depth<<5)-current_depth))<<9)/(sweep*4410L));
	    if (lfo_struct.depth_inc == 0) lfo_struct.depth_inc = 1;
	} else {
	    lfo_struct.depth_0 = (USHORT)(final_depth<<5);
	}
	iwl_lfo_write_block(voice, lfo_type, (iwl_lfo_struct RFAR *)&lfo_struct);
	LEAVE;
    }
}

void iwl_lfo_shutdown(
  int voice,
  unsigned char lfo_type)    /* IW_LFO_VIBRATO | IW_LFO_TRMELO */
{
    unsigned long lfo_block_addr;

    if (iwl_lfo_base != (unsigned long)-1) {
	ENTER;
	lfo_block_addr = LFO_BLOCK_ADDR(voice, lfo_type);
	iw_poke16(lfo_block_addr, 0);
	IWL_SET_PAGE(voice);
	if (lfo_type == IW_LFO_VIBRATO) IWL_OUT_B(SET_FREQ_LFO, 0);
	if (lfo_type == IW_LFO_TREMOLO) IWL_OUT_B(SET_VOLUME_LFO, 0);
	LEAVE;
    }
}
