
/*
**
**  $VER: dispatch.c 1.1 (9.11.97)
**  mpegaudio.datatype 1.1
**
**  Dispatch routine for a DataTypes class
**
**  Written 1996/97 by Roland 'Gizzy' Mainz
**  Original example source from David N. Junod
**
*/

/* main includes */
#include "classbase.h"
#include "classdata.h"

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

/* This switch would modify the datatype to use "mpega" */
#if 0
#define USE_MPEGA 1
#endif

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

/* local prototypes */
static                 LONG              LoadSample( struct ClassBase *, Object * );
static                 void              mysprintf( struct ClassBase *, STRPTR, STRPTR, ... );

#ifndef NO_ENCODER
static                 ULONG             SaveMPEGAudio( struct ClassBase *, Object *, struct dtWrite * );
#endif /* NO_ENCODER */

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

/* Create "mpegaudio.datatype" BOOPSI class */
struct IClass *initClass( struct ClassBase *cb )
{
    struct IClass *cl;

    /* Create our class... */
    if( cl = MakeClass( MPEGAUDIODTCLASS, SOUNDDTCLASS, NULL, (ULONG)sizeof( struct MPEGAudioInstData ), 0UL ) )
    {
#define DTSTACKSIZE (16384UL)
      cl -> cl_Dispatcher . h_Entry    = (HOOKFUNC)StackSwapDispatch; /* see stackswap.c */
      cl -> cl_Dispatcher . h_SubEntry = (HOOKFUNC)Dispatch;          /* see stackswap.c */
      cl -> cl_Dispatcher . h_Data     = (APTR)DTSTACKSIZE;           /* see stackswap.c */
      cl -> cl_UserData                = (ULONG)cb;                   /* class library base as expected by datatypes.library */

      AddClass( cl );
    }

    return( cl );
}

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

/* class dispatcher */
DISPATCHERFLAGS
ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
{
    struct ClassBase     *cb = (struct ClassBase *)(cl -> cl_UserData);
    ULONG                 retval = 0UL;

    switch( msg -> MethodID )
    {
/****** mpegaudio.datatype/OM_NEW ********************************************
*
*    NAME
*        OM_NEW -- Create a mpegaudio.datatype object.
*
*    FUNCTION
*        The OM_NEW method is used to create an instance of the
*        mpegaudio.datatype class. This method is passed to the superclass
*        first. After this, mpegaudio.datatype loads the sample using
*        "mpega" and the 8svx.datatype.
*
*    ATTRIBUTES
*        The following attributes can be specified at creation time.
*
*        DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
*            attribute. Only DTST_FILE is supported.
*            If any other type was set in a given DTA_SourceType,
*            OM_NEW will be rejected.
*            Defaults to DTST_FILE.
*
*        DTA_Handle -- For DTST_FILE, a BPTR to a lock is expected by
*            datatypesclass, which will convert this into a filehandle.
*            A DTST_RAM (create empty object) source type requires a NULL
*            handle.
*
*    NOTE
*        If the datatype was compiled with the NO_ENCODER flag set,
*        DTA_SourceType == DTST_RAM causes OM_NEW to reject the method.
*
*    RESULT
*        If the object was created a pointer to the object is returned,
*        otherwise NULL is returned.
*
******************************************************************************
*
*/
      case OM_NEW:
      {
          struct TagItem *ti;

          /* We only support DTST_FILE or DTST_RAM as source type */
          if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
          {
            if( ((ti -> ti_Data) != DTST_FILE)
#ifndef NO_ENCODER
&& ((ti -> ti_Data) != DTST_RAM)
#endif /* !NO_ENCODER */

)
            {
              SetIoErr( ERROR_OBJECT_WRONG_TYPE );

              break;
            }
          }

          /* This must not be a subclass of mpegaudio.datatype
           * (not implemented yet)
           */
          if( o == (Object *)cl )
          {
            if( retval = DoSuperMethodA( cl, o, msg ) )
            {
              LONG error;

              /* Load sample... */
              if( error = LoadSample( cb, (Object *)retval ) )
              {
                /* Something went fatally wrong, dispose object */
                CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
                retval = 0UL;
              }

              SetIoErr( error );
            }
          }
          else
          {
            /* Subclasses of mpegaudio.datatype are not implemented */
            SetIoErr( ERROR_NOT_IMPLEMENTED );
          }
      }
          break;

/****** mpegaudio.datatype/OM_DISPOSE ****************************************
*
*    NAME
*        OM_DISPOSE -- Delete object
*
*    FUNCTION
*        Frees the contents of the mpegvideo.datatype instance data
*        and passes then the msg to the superclass.
*
*        This method deletes the embedded 8svx.datatype object and deletes
*        the temporary 8SVX sample file created.
*
*    RESULT
*        Returns 0 evertimes.
*
******************************************************************************
*
*/
      case OM_DISPOSE:
      {
          struct MPEGAudioInstData *maid        = (struct MPEGAudioInstData *)INST_DATA( cl, o );
          LONG                      saved_ioerr = IoErr(); /* save I/O error */

          /* Any embedded 8SVX/AIFF object ? */
#ifdef USE_MPEGA
          if( maid -> maid_AIFFObject )
          {
            /* Avoid that we free the sample data which belongs to the aiff.datatype object */
            SetDTAttrs( o, NULL, NULL, SDTA_Sample, NULL, TAG_DONE );

            DisposeDTObject( (maid -> maid_AIFFObject) );
          }

          /* Delete tmp AIFF file if we created one... */
          if( strlen( (maid -> maid_AIFFName) ) )
          {
            DeleteFile( (maid -> maid_AIFFName) );
          }
#else
          if( maid -> maid_8SVXObject )
          {
            /* Avoid that we free the sample data which belongs to the 8svx.datatype object */
            SetDTAttrs( o, NULL, NULL, SDTA_Sample, NULL, TAG_DONE );

            DisposeDTObject( (maid -> maid_8SVXObject) );
          }

          /* Delete tmp 8SVX file if we created one... */
          if( strlen( (maid -> maid_8SVXName) ) )
          {
            DeleteFile( (maid -> maid_8SVXName) );
          }
#endif /* USE_MPEGA */

          /* Free outselves */
          DoSuperMethodA( cl, o, msg );

          /* Restore I/O error */
          SetIoErr( saved_ioerr );
      }
          break;

      case OM_UPDATE:
      {
          if( DoMethod( o, ICM_CHECKLOOP ) )
          {
            break;
          }
      }
      case OM_SET:
      {
          /* Pass the attributes to the sound class and force a refresh if we need it */
          if( retval = DoSuperMethodA( cl, o, msg ) )
          {
/* The following check statement isn't needed because OM_NEW does not allow subclasses of mpegaudio.datatype,
 * therefore, we're always the top instance...
 */
#ifdef COMMENTED_OUT
            /* Top instance ? */
            if( OCLASS( o ) == cl )
#endif /* COMMENTED_OUT */
            {
              struct RastPort *rp;

              /* Get a pointer to the rastport */
              if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
              {
                struct gpRender gpr;

                /* Force a redraw */
                gpr . MethodID   = GM_RENDER;
                gpr . gpr_GInfo  = ((struct opSet *)msg) -> ops_GInfo;
                gpr . gpr_RPort  = rp;
                gpr . gpr_Redraw = GREDRAW_UPDATE;

                DoMethodA( o, (Msg)(&gpr) );

                /* Release the temporary rastport */
                ReleaseGIRPort( rp );

                /* We did a refresh... */
                retval = 0UL;
              }
            }
          }
      }
          break;

/****** mpegaudio.datatype/DTM_WRITE **********************************************
*
*    NAME
*        DTM_WRITE -- Save data
*
*    FUNCTION
*        This method saves the object's contents to disk.
*
*        If dtw_Mode is DTWM_IFF, the method is passed unchanged to the
*        superclass, sound.datatype, which writes an IFF 8SVX sample.
*
*        If dtw_mode is DTWM_RAW, the object writes a MPEG audio stream to
*        the filehandle given.
*        (If the class library was compiled with the NO_ENCODER switch
*        (not the default), result == 0 and resul2 == ERROR_NOT_IMPLEMENTED
*        are returned).
*
*    TAGS
*        None defined.
*
*    RESULT
*        Returns 0 for failure (IoErr() returns result2), non-zero
*        for success.
*
******************************************************************************
*
*/
      case DTM_WRITE:
      {
          struct dtWrite *dtw;

          dtw = (struct dtWrite *)msg;

          /* Local data format requested ? */
          if( (dtw -> dtw_Mode) == DTWM_RAW )
          {
/* Enable the followng code if you don't have an encoder implemented... */
#ifdef NO_ENCODER
            SetIoErr( ERROR_NOT_IMPLEMENTED );
            retval = 0UL;
#else
            retval = SaveMPEGAudio( cb, o, dtw );
#endif /* NO_ENCODER */
          }
          else
          {
            /* Pass msg to superclass (which writes an IFF 8SVX sample)... */
            retval = DoSuperMethodA( cl, o, msg );
          }
      }
          break;

      /* Let the superclass handle everything else */
      default:
      {
          retval = DoSuperMethodA( cl, o, msg );
      }
          break;
    }

    return( retval );
}


static
LONG LoadSample( struct ClassBase *cb, Object *o )
{
    struct MPEGAudioInstData *maid  = (struct MPEGAudioInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
    LONG                      error = 0L;
    BPTR                      fh;               /* stream handle                                */
    ULONG                     sourcetype;       /* type of stream (either DTST_FILE or DTST_RAM */
    struct VoiceHeader       *vh;               /* obj's voice header                           */

    /* Get file handle, handle type and VoiceHeader */
    if( GetDTAttrs( o, DTA_SourceType,    (&sourcetype),
                       DTA_Handle,        (&fh),
                       SDTA_VoiceHeader,  (&vh),
                       TAG_DONE ) == 3UL )
    {
      switch( sourcetype )
      {
        case DTST_FILE:
        {
            /* We got all what we want (a filehandle) */
        }
            break;

#ifndef NO_ENCODER
        case DTST_RAM:
        {
            /* Do nothing... */
        }
            break;
#endif /* !NO_ENCODER */

        default:
        {
            /* unsupported source type */
            error = ERROR_NOT_IMPLEMENTED;
        }
            break;
      }

      /* Any error ? */
      if( error == 0L )
      {
#ifdef USE_MPEGA
        if( fh )
        {
          TEXT pipe_in[ 256 ],
               cmd[ 256 ];
          BPTR mpega_in;

          mysprintf( cb, pipe_in,                 "PIPE:MPEGAudio_in%lx",    o );
          mysprintf( cb, (maid -> maid_AIFFName), "T:MPEGAudio_tmp%lx.aiff", o );
          mysprintf( cb, cmd,                     "mpega:mpega -A -d4 -q0 -r -s -i -u -m -D -C %s %s", pipe_in, (maid -> maid_AIFFName) );

          if( mpega_in = Open( pipe_in, MODE_NEWFILE ) )
          {
            /* Start decoder "mpega" */
            if( SystemTags( cmd, SYS_Input,  Open( "CON:////MPEG Audio DataType/auto/close/wait/inactive", MODE_NEWFILE ),
                                 SYS_Output, NULL,
                                 SYS_Asynch, TRUE,
                                 TAG_DONE ) == 0L )
            {
              UBYTE buffer[ 4096 ];
              LONG  numread;

              /* Feed "mpega" with data (througth the pipe) */
              do
              {
                if( (numread = Read( fh, buffer, sizeof( buffer ) )) != -1L )
                {
                  (void)Write( mpega_in, buffer, numread );
                }
              } while( numread == sizeof( buffer ) );
            }
            else
            {
              /* can't start "mpega" */
              error = IoErr();
            }

            Close( mpega_in );
          }
          else
          {
            /* can't open pipe */
            error = IoErr();
          }

          /* Success ? */
          if( error == 0L )
          {
try:
            maid -> maid_AIFFObject = NewDTObject( (maid -> maid_AIFFName), DTA_GroupID, GID_SOUND, TAG_DONE );

            /* This may happen if "mpega" has not been finished yet,,, */
            if( ((maid -> maid_AIFFObject) == NULL) && (IoErr() == ERROR_OBJECT_IN_USE) )
            {
              /* Wait a 1/2 sec */
              Delay( (TICKS_PER_SECOND / 2UL) );
              goto try;
            }

            if( maid -> maid_AIFFObject )
            {
              struct VoiceHeader *aiff_vh;
              APTR                aiff_sample;
              ULONG               aiff_samplelength;
              ULONG               aiff_period;
              ULONG               aiff_volume;

              if( GetDTAttrs( (maid -> maid_AIFFObject), SDTA_VoiceHeader,    (&aiff_vh),
                                                         SDTA_Sample,         (&aiff_sample),
                                                         SDTA_SampleLength,   (&aiff_samplelength),
                                                         SDTA_Period,         (&aiff_period),
                                                         SDTA_Volume,         (&aiff_volume),
                                                         TAG_DONE ) == 5UL )
              {
                /* Copy voiceheader. This point is very tricky because mpeg audio might not support all things... */
                *vh = *aiff_vh;

                /* Set misc attributes
                 * In fact, SDTA_Period and SDTA_Volume are calculated from SDTA_VoiceHeader info,
                 * but we set it here EXPLICITLY that noone can say we didn't pass this to sound.datatype...
                 */
                SetDTAttrs( o, NULL, NULL, SDTA_Sample,         aiff_sample,
                                           SDTA_SampleLength,   aiff_samplelength,
                                           SDTA_Period,         aiff_period,
                                           SDTA_Volume,         aiff_volume,
                                           TAG_DONE );
              }
              else
              {
                /* the object did not support all attributes we want... */
                error = ERROR_OBJECT_WRONG_TYPE;
              }
            }
            else
            {
              /* NewDTObject failed */
              error = IoErr();
            }
          }
        }
        else
        {
          /* no filehandle */
          error = ERROR_NO_FREE_STORE;
        }
#else
        if( fh )
        {
          BPTR stdout      = Open( "CON:////MPEG Audio DataType/auto/close/wait/inactive", MODE_NEWFILE );
          TEXT cmd[ 256 ];

          /* The '@' character at the beginning of the input filename option 
           * means in my "maplay" version to use stdin as input
           */
          mysprintf( cb, (maid -> maid_8SVXName), "@%lx.8svx", o );
          mysprintf( cb, cmd,                     "maplay:maplay -v @%lx", o );

          /* Start decoder "maplay" */
          if( SystemTags( cmd, SYS_Input,      fh,
                               SYS_Output,     stdout,
                               NP_ConsoleTask, (((struct FileHandle *)BADDR( stdout )) -> fh_Type),
                               NP_StackSize,   16384UL,
                               TAG_DONE ) == 0L )
          {
          }
          else
          {
            /* can't start "maplay" */
            error = IoErr();
          }

          Close( stdout );

          /* Success ? */
          if( error == 0L )
          {
try:
            maid -> maid_8SVXObject = NewDTObject( (maid -> maid_8SVXName), DTA_GroupID, GID_SOUND, TAG_DONE );

            /* This may happen if "maplay" has not been finished yet,,, */
            if( ((maid -> maid_8SVXObject) == NULL) && (IoErr() == ERROR_OBJECT_IN_USE) )
            {
              /* Wait a 1/5 sec */
              Delay( (TICKS_PER_SECOND / 5UL) );
              goto try;
            }

            if( maid -> maid_8SVXObject )
            {
              struct VoiceHeader *iff8svx_vh;
              APTR                iff8svx_sample;
              ULONG               iff8svx_samplelength;
              ULONG               iff8svx_period;
              ULONG               iff8svx_volume;

              if( GetDTAttrs( (maid -> maid_8SVXObject), SDTA_VoiceHeader,    (&iff8svx_vh),
                                                         SDTA_Sample,         (&iff8svx_sample),
                                                         SDTA_SampleLength,   (&iff8svx_samplelength),
                                                         SDTA_Period,         (&iff8svx_period),
                                                         SDTA_Volume,         (&iff8svx_volume),
                                                         TAG_DONE ) == 5UL )
              {
                /* Copy voiceheader. This point is very tricky because mpeg audio might not support all things... */
                *vh = *iff8svx_vh;

                /* Set misc attributes
                 * In fact, SDTA_Period and SDTA_Volume are calculated from SDTA_VoiceHeader info,
                 * but we set it here EXPLICITLY that noone can say we didn't pass this to sound.datatype...
                 */
                SetDTAttrs( o, NULL, NULL, SDTA_Sample,         iff8svx_sample,
                                           SDTA_SampleLength,   iff8svx_samplelength,
                                           SDTA_Period,         iff8svx_period,
                                           SDTA_Volume,         iff8svx_volume,
                                           TAG_DONE );
              }
              else
              {
                /* the object did not support all attributes we want... */
                error = ERROR_OBJECT_WRONG_TYPE;
              }
            }
            else
            {
              /* NewDTObject failed */
              error = IoErr();
            }
          }
        }
        else
        {
          /* no filehandle */
          error = ERROR_NO_FREE_STORE;
        }
#endif /* USE_MPEGA */
      }
    }
    else
    {
      /* can't get required attributes from superclass */
      error = ERROR_OBJECT_WRONG_TYPE;
    }

    return( error );
}


static
void mysprintf( struct ClassBase *cb, STRPTR buffer, STRPTR fmt, ... )
{
    APTR args;

    args = (APTR)((&fmt) + 1);

    RawDoFmt( fmt, args, (void (*))"\x16\xc0\x4e\x75", buffer );
}


#ifndef NO_ENCODER
/* The MPEG Audio encoder */
static
ULONG SaveMPEGAudio( struct ClassBase *cb, Object *o, struct dtWrite *dtw )
{
    ULONG retval = 0UL;
    LONG  error  = ERROR_NOT_IMPLEMENTED;

#if 0
    /* A NULL file handle is a nop (GMultiView uses this to test if a datatype supports RAW writing) */
    if( dtw -> dtw_FileHandle )
    {
    }
#endif

    /* Store Result2 */
    SetIoErr( error );

    return( retval );
}
#endif /* !NO_ENCODER */





