/*
** Module:    Sound.
** Author:    Paul Manias
** Copyright: DreamWorld Productions (c) 1996-1998.  All rights reserved.
**
** --------------------------------------------------------------------------
** 
** TERMS AND CONDITIONS
** 
** This source code is made available on the condition that it is only used to
** further enhance the Games Master System.  IT IS NOT DISTRIBUTED FOR THE USE
** IN OTHER PRODUCTS.  Developers may edit and re-release this source code
** only in the form of its GMS module.  Use of this code outside of the module
** is not permitted under any circumstances.
** 
** This source code stays the copyright of DreamWorld Productions regardless
** of what changes or additions are made to it by 3rd parties.  A joint
** copyright can be granted if the 3rd party wishes to retain some ownership
** of said modifications.
** 
** In exchange for our distribution of this source code, we also ask you to
** distribute the source when releasing a modified version of this module.
** This is not compulsory if any additions are sensitive to 3rd party
** copyrights, or if it would damage any commercial product(s).
** 
** --------------------------------------------------------------------------
**
** BUGS AND MISSING FEATURES
** -------------------------
** If you correct a bug or fill in a missing feature, the source should be
** e-mailed to pmanias@ihug.co.nz for inclusion in the next update of this
** module.
**
** + Support for Sound->Frequency.
**
** + WAVE Support - to be implemented as a child module (ie not in this
**   source code).
**
** CHANGES
** -------
** -1997-
** 27 Oct Replaced IFF body search with better one from Screens module.
**        Added support for CheckFile().
** 30 Oct Optimised the way audio channels are allocated from the OS.
**        Sound now supports Deactivate().
**        Edited too many parts to note!  Document the flags.
** 31 Oct Implemented SLEFT, SRIGHT, SFORCE.
**
** -1998-
** 29 Jan Added a Pair field for modulation.
** 14 Mar Removed the Sound->Header field.
** 18 Jul Moved precalculated arrays from asm to C.
**        Removed data=faronly from the SCOPTIONS file.
**        Added support for field orientation.
*/

#include <proto/dpkernel.h>
#include <system/all.h>
#include <dpkernel/prefs.h>
#include "defs.h"

/***********************************************************************************/

BYTE * AttribFlags[10] = {
  "BIT16",
  "MODVOL",
  "MODPER",
  "REPEAT",
  "EMPTY",
  "LEFT",
  "RIGHT",
  "FORCE",
  "STOPLAST",
  NULL
};

#define SND_FIELDS 9

struct Field SoundFields[SND_FIELDS] = {
  { "Priority",  14, FID_Priority,  FDF_WORD|FDF_RANGE, 0, 1000 },
  { "Data",      16, FID_Data,      FDF_BYTEARRAY,      0, 0 },
  { "Length",    20, FID_Length,    FDF_LONG,           0, 0 },
  { "Octave",    24, FID_Octave,    FDF_WORD|FDF_RANGE, 0, 1000 }, 
  { "Volume",    26, FID_Volume,    FDF_WORD|FDF_RANGE, 0, 100 },
  { "Attrib",    28, FID_Attrib,    FDF_LONG|FDF_FLAGS, (LONG)AttribFlags, 0 },
  { "Source",    32, FID_Source,    FDF_SOURCE,         0, 0 },
  { "Frequency", 36, FID_Frequency, FDF_LONG,           0, 0 },
  { "Pair",      40, FID_Sound,     FDF_OBJECT,         ID_SOUND, 0 },
};

struct Function JumpTableV1[] = {
  { LIBAllocSoundMem, "AllocSoundMem(d0l,d1l)" },
  { LIBStopAudio,     "StopAudio()"            },
  { LIBCheckSound,    "CheckSound(a0l)"        },
  { LIBFreeSoundMem,  "FreeSoundMem(d0l)"      },
  { LIBSetVolume,     "SetVolume(a0l,d0w)"     },
  { NULL, NULL }
};

BYTE ModAuthor[]    = "Paul Manias";
BYTE ModDate[]      = "July 1998";
BYTE ModCopyright[] = "DreamWorld Productions (c) 1996-1998.  All rights reserved.";
BYTE ModName[]      = "Sound Module";

/************************************************************************************
** Command:  Init()
**
** Called when our module is being opened for the first time.
*/

LIBFUNC LONG CMDInit(mreg(__a0) LONG argModule,
                     mreg(__a1) LONG argDPKBase,
                     mreg(__a2) LONG argGVBase,
                     mreg(__d0) LONG argDPKVersion,
                     mreg(__d1) LONG argDPKRevision)
{
  APTR *Exec = (APTR *)4;

  DPKBase = (APTR)argDPKBase;
  GVBase  = (struct GVBase *)argGVBase;
  Public  = ((struct Module *)argModule)->Public;
  SysBase = Exec[0];
  FileMod = NULL;

  if ((argDPKVersion < DPKVersion) OR
     ((argDPKVersion IS DPKVersion) AND (argDPKRevision < DPKRevision))) {
     DPrintF("!Sound:","This module requires V%d.%d of the dpkernel.library.",DPKVersion,DPKRevision);
  }
  else {
     if (FileMod = Get(ID_MODULE|GET_NOTRACK)) {
        FileMod->Number = MOD_FILES;
        if (Init(FileMod,NULL)) {
           FILBase = FileMod->ModBase;

           if (SndObject = AddSysObjectTags(ID_SOUND, ID_SOUND, "Sound",
                 TAGS, NULL,
                 SOA_FileExtension, "iff;8svx;snd",
                 SOA_FileDesc,      "IFF Sound Sample",
                 SOA_Activate,      SND_Activate,
                 SOA_CheckFile,     SND_CheckFile,
                 SOA_Deactivate,    SND_Deactivate,
                 SOA_CopyToUnv,     SND_CopyToUnv,
                 SOA_CopyFromUnv,   SND_CopyFromUnv,
                 SOA_Free,          SND_Free,
                 SOA_Get,           SND_Get,
                 SOA_Init,          SND_Init,
                 SOA_Load,          SND_Load,
                 SOA_FieldArray,    SoundFields,
                 SOA_FieldTotal,    SND_FIELDS,
                 TAGEND)) {


              return(ERR_OK);
           }
        }
     }
  }

  FreeModule();
  return(ERR_FAILED);
}

/************************************************************************************
** Command:  Open()
**
** Called when our module is being opened for a second time...
*/

LIBFUNC LONG CMDOpen(mreg(__a0) struct Module *Module)
{
  if ((Module) AND (Public)) {
     Module->FunctionList = JumpTableV1;
     Public->OpenCount++;
     return(ERR_OK);
  }
  else return(ERR_FAILED);
}

/************************************************************************************
** Command:  Expunge()
** Synopsis: LONG Expunge(void);
**
** Called on expunge - if no program has us opened then we can give permission to
** have us shut us down.
**
*/

LIBFUNC LONG CMDExpunge(void)
{
  if (Public) {
     if (Public->OpenCount IS NULL) {
        FreeModule();
        return(ERR_OK); /* Okay to expunge */
     }
  }
  else DPrintF("!Sound:","I have no Public base reference.");

  return(ERR_FAILED); /* Do not expunge */
}

/************************************************************************************
** Command:  Close()
** Synopsis: void Close(*Module [a0]);
*/

LIBFUNC void CMDClose(mreg(__a0) struct Module *Module)
{
  if (Public) Public->OpenCount--;
}

/************************************************************************************
** Internal: FreeModule()
**
** Frees any allocations made in the opening of our module.
*/

void FreeModule(void) {
  if (SndObject) {
     RemSysObject(SndObject);
     SndObject = NULL;
  }

  if (FileMod) {
     Free(FileMod);
     FileMod = NULL;
  }
}

/************************************************************************************
** Internal: FindHeader
** Synopsis: Chunk = FindHeader(FORM);
*/

APTR FindHeader(LONG *form, LONG ID)
{
   BYTE *endiff = (BYTE *)form;
   BYTE *bytetmp;

   if ((form[0] IS CODE_FORM) AND (ID != NULL)) {
      endiff += form[1] + 8;     /* Find the end */
      form   += 3;               /* Skip FORM/Size/ILBM */

      while (form < (LONG *)endiff) {
         if (form[0] IS ID) {
            return(form+2);
         }

         bytetmp = (BYTE *)form + form[1] + 8;
         form    = (LONG *)bytetmp;

         /* Check for an uneven offset, if detected then
         ** add an extra 1 to make it all even.
         */

         if ((LONG)form & 0x00000001) {
            form = (LONG *)((LONG)form + 1);
         }
      }
      DPrintF("FindHeader:","Failed to find IFF chunk $%x.",ID);
   }
   else ErrCode(ERR_ARGS);

   return(NULL);
}

#include "SND_CopyStructure.c"
#include "SND_Init.c"
#include "SND_Misc.c"
#include "LIB_Memory.c"

