home *** CD-ROM | disk | FTP | other *** search
-
- /*
- **
- ** $VER: dispatch.c 2.2 (1.5.98)
- ** mpegaudio.datatype 2.2
- **
- ** Dispatch routine for a DataTypes class
- **
- ** Written 1996-1998 by Roland 'Gizzy' Mainz
- ** Original example source from David N. Junod
- **
- */
-
- /* main includes */
- #include "classbase.h"
- #include "classdata.h"
-
- /*****************************************************************************/
-
- /* local prototypes */
- static DISPATCHERFLAGS ULONG Dispatch( REGA0 struct IClass *, REGA2 Object *, REGA1 Msg );
- static BOOL LoadSample( struct ClassBase *, Object * );
-
- #ifndef NO_ENCODER
- static ULONG SaveMPEGAudio( struct ClassBase *, Object *, struct dtWrite * );
- #endif /* NO_ENCODER */
-
- static DISPATCHERFLAGS ULONG bitstream_hook( REGA0 struct Hook *, REGA2 APTR , REGA1 MPEGA_ACCESS * );
-
-
- /*****************************************************************************/
-
- /* 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 */
- static
- 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.library"'s decoder into memory.
- *
- * 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 ) )
- {
- /* Load sample... */
- if( !LoadSample( cb, (Object *)retval ) )
- {
- /* Something went fatally wrong, dispose object */
- CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
- retval = 0UL;
- }
- }
- }
- 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.
- *
- * RESULT
- * Returns 0 evertimes.
- *
- ******************************************************************************
- *
- */
- case OM_DISPOSE:
- {
- LONG saved_ioerr = IoErr(); /* save I/O error */
-
- /* Free ourselves */
- 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
- BOOL LoadSample( struct ClassBase *cb, Object *o )
- {
- struct MPEGAudioInstData *maid = (struct MPEGAudioInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
- LONG error = 0L;
- BOOL success = FALSE;
- 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 )
- {
- if( fh )
- {
- BYTE *sample = NULL;
- LONG pcm_pos = 0L; /* position in buffer */
- struct Hook accesshook = { 0 };
-
- MPEGA_CTRL mpa_ctrl =
- {
- NULL, // Bitstream access hook, see below (set to (&accesshook))
- // Layers I & II settings (mono, stereo)
- { FALSE, { 2, 0, 20000 }, { 2, 0, 20000 } },
- // Layer III settings (mono, stereo)
- { FALSE, { 2, 0, 20000 }, { 2, 0, 20000 } },
- 0, // Don't check mpeg validity at start (needed for mux stream)
- 1024 // Stream Buffer size
- };
-
- ULONG samplesize = 0UL;
-
- /* init hook */
- accesshook . h_Entry = (HOOKFUNC)bitstream_hook;
- accesshook . h_SubEntry = (HOOKFUNC)cb; /* misused here as 2nd data field... */
- accesshook . h_Data = (APTR)fh;
- mpa_ctrl . bs_access = (&accesshook);
-
- if( maid -> maid_MPAS = MPEGA_open( NULL, (&mpa_ctrl) ) )
- {
- WORD left[ MPEGA_PCM_SIZE + 32 ],
- right[ MPEGA_PCM_SIZE + 32 ],
- *pcm[ 2 ];
- LONG pcm_count;
-
- pcm[ 0 ] = left;
- pcm[ 1 ] = right;
-
- /* Decoder frames into a continous ram buffer; if the buffer gets to small, enlarge it... */
- while( (pcm_count = MPEGA_decode_frame( (maid -> maid_MPAS), pcm )) >= 0 )
- {
- BYTE *newsample;
- ULONG next = pcm_pos + pcm_count + 32;
-
- if( samplesize <= next )
- {
- next += MPEGA_PCM_SIZE * 256UL;
-
- if( newsample = (BYTE *)AllocVec( (next + 256), MEMF_PUBLIC ) )
- {
- /* "sample" and "newsample" are long-word aligned, and "pcm_pos" can be padded without trouble,
- * therefore I can use CopyMemQuick here...
- */
- CopyMemQuick( sample, newsample, ((pcm_pos + 3UL) & ~3UL) );
- FreeVec( sample );
- sample = newsample;
- samplesize = next;
- }
- else
- {
- /* no buffer memory */
- pcm_count = MPEGA_ERR_MEM;
- break;
- }
- }
-
- #ifdef COMNTED_OUT
- /* KISS PCM->sample conversion function */
- {
- LONG i;
-
- /* Stereo ? */
- if( (maid -> maid_MPAS -> channels) == 2 )
- {
- /* Convert left+right channels */
- for( i = 0L ; i < pcm_count ; i++ )
- {
- sample[ i + pcm_pos ] = ((left[ i ] + right[ i ]) >> 9); /* ((left + right) / 2) >> 8 */
- }
- }
- else
- {
- /* Convert left channel (mono) only */
- for( i = 0L ; i < pcm_count ; i++ )
- {
- sample[ i + pcm_pos ] = (left[ i ] >> 8); /* ((left + right) / 2) >> 8 */
- }
- }
- }
- #else
- /* This replacement for the loop above is much faster because
- * the ratio "loop-check vs. PCM->sample-conversion" is ~~ 1:8
- * instead of 1:1 (like above)...
- */
- {
- /* Stereo ? */
- if( (maid -> maid_MPAS -> channels) == 2 )
- {
- /* Convert left+right channels */
- register ULONG blocks = (pcm_count & ~(0x07UL)) >> 3UL,
- cut = pcm_count & (0x07UL),
- i;
- register BYTE *sptr = sample + pcm_pos;
- register WORD *lbptr = left,
- *rbptr = right;
-
- /* Copy sample blocks... */
- for( i = 0UL ; i < blocks ; i++ )
- {
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- }
-
- /* ... then the remaining samples. */
- for( i = 0UL ; i < cut ; i++ )
- {
- *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
- }
- }
- else
- {
- /* Convert left channel (mono) only */
- register ULONG blocks = (pcm_count & ~(0x07UL)) >> 3UL,
- cut = pcm_count & (0x07UL),
- i;
- register BYTE *sptr = sample + pcm_pos;
- register WORD *lbptr = left;
-
- /* Copy sample blocks... */
- for( i = 0UL ; i < blocks ; i++ )
- {
- *sptr++ = (*lbptr++ >> 8);
- *sptr++ = (*lbptr++ >> 8);
- *sptr++ = (*lbptr++ >> 8);
- *sptr++ = (*lbptr++ >> 8);
- *sptr++ = (*lbptr++ >> 8);
- *sptr++ = (*lbptr++ >> 8);
- *sptr++ = (*lbptr++ >> 8);
- *sptr++ = (*lbptr++ >> 8);
- }
-
- /* ... then the remaining samples. */
- for( i = 0UL ; i < cut ; i++ )
- {
- *sptr++ = (*lbptr++ >> 8);
- }
- }
- }
- #endif /* COMMENTED_OUT */
-
- pcm_pos += pcm_count;
- }
-
- /* dispatch error code... */
- switch( pcm_count )
- {
- case MPEGA_ERR_NONE: break;
- case MPEGA_ERR_EOF: break;
- case MPEGA_ERR_BADFRAME: error = DTERROR_INVALID_DATA; break;
- case MPEGA_ERR_MEM: error = ERROR_NO_FREE_STORE; break;
- case MPEGA_ERR_NO_SYNC: error = DTERROR_INVALID_DATA; break;
- }
-
- /* No error ? */
- if( !error )
- {
- ULONG clock = ((SysBase -> ex_EClockFrequency) * 5UL); /* amiga clock */
- ULONG period = clock / (maid -> maid_MPAS -> dec_frequency);
-
- /* Set up voice header */
- vh -> vh_OneShotHiSamples = clock / period;
- vh -> vh_RepeatHiSamples = 0UL;
- vh -> vh_SamplesPerHiCycle = 0UL;
- vh -> vh_SamplesPerSec = clock / period;
- vh -> vh_Octaves = 1UL;
- vh -> vh_Compression = CMP_NONE;
- vh -> vh_Volume = 0x10000UL; /* maximum volume */
-
- /* 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, sample,
- SDTA_SampleLength, pcm_pos,
- SDTA_Period, period,
- TAG_DONE );
-
- sample = NULL; /* The object now owns the sample data */
-
- /* All done... */
- success = TRUE;
- }
-
- MPEGA_close( (maid -> maid_MPAS) );
- }
-
- FreeVec( sample );
- }
- else
- {
- /* no filehandle */
- error = ERROR_NO_FREE_STORE;
- }
- }
- }
- else
- {
- /* can't get required attributes from superclass */
- error = ERROR_OBJECT_WRONG_TYPE;
- }
-
- SetIoErr( error );
-
- return( success );
- }
-
-
- #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 */
-
-
- /* mpega.library bitstream hook
- * Required because mpega.library has no way to access an already open filehandle and
- * because we're not allowed to close a filehandle which is owned by "datatypesclass".
- */
- static
- DISPATCHERFLAGS
- ULONG bitstream_hook( REGA0 struct Hook *hook, REGA2 APTR handle, REGA1 MPEGA_ACCESS *access )
- {
- struct ClassBase *cb = (struct ClassBase *)(hook -> h_SubEntry);
- ULONG retval = 0UL;
-
- switch( access -> func )
- {
- case MPEGA_BSFUNC_OPEN:
- {
- /* We don't know the total size :-( */
- access -> data . open . stream_size = 0L;
-
- /* Return handle */
- retval = (ULONG)(hook -> h_Data);
- }
- break;
-
- case MPEGA_BSFUNC_CLOSE: /* NOP */
- break;
-
- case MPEGA_BSFUNC_READ:
- {
- /* Check valid handle */
- if( handle )
- {
- retval = Read( (BPTR)handle, (access -> data . read . buffer), (access -> data . read . num_bytes) );
- }
- }
- break;
-
- case MPEGA_BSFUNC_SEEK:
- {
- if( handle )
- {
- retval = (Seek( (BPTR)handle, (access -> data . seek . abs_byte_seek_pos), OFFSET_BEGINNING ) == -1L);
- }
- }
- break;
- }
-
- return( retval );
- }
-
-
-
-
-