QTSndTween.c

//
//  File:       QTSndTween.c
//
//  Contains:   Sound tweening support for QuickTime movies.
//
//  Written by: Tim Monroe
//              based largely on the tween sample code in the QuickTime 2.5 Developers Guide
//              and the sample code QT3DTween.c.
//
//  Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <3>      03/21/00    rtm     made changes for running under CarbonLib
//     <2>      05/26/98    rtm     set the time scale of the sound track media to that of the movie,
//                                  using revised code from Peter Hoddie 
//     <1>      04/10/98    rtm     first file; revised to personal coding style
//     
//
// This sample shows how to modify an existing QuickTime movie so that the volume of its sound track
// is gradually increased (or decreased) as the movie plays. We do this by adding a tween track to
// the movie and linking the tween track to the existing sound track.
// 
// NOTES:
//
// *** (1) ***
// For complete information on creating tween media tracks, see the chapter "Tween Media Handler Components"
// in the QuickTime 2.5 or 3.0 Developers Guide.
//
//
 
#include <FixMath.h>
#include <Sound.h>
 
#include "QTSndTween.h"
 
// global variables
// this variable determines what kind of tween data we add to the movie
 
Boolean             gTweenLoToHigh = true;      // do we start at no volume and end at full volume?
 
 
//////////
//
// QTSndTween_AddTweenTrackToMovie
// Add a tween track to a QuickTIme movie.
//
//////////
 
OSErr QTSndTween_AddTweenTrackToMovie (Movie theMovie)
{
    Track                       mySndTrack = NULL;
    Track                       myTrack;
    Media                       myMedia;
    StandardFileReply           myReply;
    SampleDescriptionHandle     mySampleDesc = NULL;
    QTAtomContainer             mySample = NULL;
    QTAtomContainer             myInputMap = NULL;
    QTAtom                      myAtom = 0;
    QTAtom                      myInputAtom = 0;
    short                       myTweenData[2];
    long                        myRefIndex;
    TimeRecord                  myTimeRec;
    OSErr                       myErr = noErr;
 
    if (theMovie == NULL)
        goto bail;
        
    //////////
    //
    // get the (first) sound track from the movie
    //
    //////////
    
    mySndTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
    if (mySndTrack == NULL)
        goto bail;
 
    //////////
    //
    // create the tween track and media, and a sample to contain the tween data
    //
    //////////
 
    // create the tween track and media
    myTrack = NewMovieTrack(theMovie, 0, 0, kNoVolume);
    myMedia = NewTrackMedia(myTrack, TweenMediaType, kTweenTimeScale, NULL, 0);
 
    // create a new sample; this sample will hold the tween data
    myErr = QTNewAtomContainer(&mySample);
    if (myErr != noErr)
        goto bail;
        
    myTweenData[0] = gTweenLoToHigh ? EndianU16_NtoB(0)     : EndianU16_NtoB(512);
    myTweenData[1] = gTweenLoToHigh ? EndianU16_NtoB(512)   : EndianU16_NtoB(0);
        
    // make a tween entry for that data;
    // a tween entry is a parent atom that contains leaf atoms describing the tweening operation
    myErr = QTSndTween_AddTweenEntryToSample(mySample, kSoundTweenID, kTweenTypeShort, &myTweenData, sizeof(myTweenData));
    if (myErr != noErr)
        goto bail;
        
    // create the sample description
    mySampleDesc = (SampleDescriptionHandle)NewHandleClear(sizeof(SampleDescription));
    if (mySampleDesc == NULL)
        goto bail;
    
    (**mySampleDesc).descSize = sizeof(SampleDescription);
 
    // add the tween sample to the media
    BeginMediaEdits(myMedia);
 
    // set the time scale of the media to that of the movie
    myTimeRec.value.lo = GetTrackDuration(mySndTrack);
    myTimeRec.value.hi = 0;
    myTimeRec.scale = GetMovieTimeScale(theMovie);
    ConvertTimeScale(&myTimeRec, GetMediaTimeScale(myMedia));
 
    myErr = AddMediaSample(myMedia, mySample, 0, GetHandleSize(mySample), myTimeRec.value.lo, (SampleDescriptionHandle)mySampleDesc, 1, 0, NULL);
    if (myErr != noErr)
        goto bail;
 
    EndMediaEdits(myMedia);
 
    // add the media to the track
    InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
 
    // dispose of some things we no longer need
    QTDisposeAtomContainer(mySample);
    DisposeHandle((Handle)mySampleDesc);
 
    //////////
    //
    // create a link between the sound track and the tween track, and update the sound track's input map
    //
    //////////
    
    // first, create a new atom container
    myErr = QTNewAtomContainer(&myInputMap);
    if (myErr != noErr)
        goto bail;
    
    // for *each* tween entry in the tween media sample,    
    // create a link between the sound track and the tween track, and add an entry to the input map
    
    myErr = AddTrackReference(mySndTrack, myTrack, kTrackModifierReference, &myRefIndex);
    if (myErr != noErr)
        goto bail;
        
    myErr = QTSndTween_AddTweenEntryToInputMap(myInputMap, myRefIndex, kSoundTweenID, kTrackModifierTypeVolume, NULL);
    if (myErr != noErr)
        goto bail;
 
    // attach the input map to the sound media
    myErr = SetMediaInputMap(GetTrackMedia(mySndTrack), myInputMap);
    if (myErr != noErr)
        goto bail;
    
    // dispose of the input map
    QTDisposeAtomContainer(myInputMap);
    
    //////////
    //
    // finish up
    //
    //////////
    
    // save the new movie file; if you are running under CarbonLib, change this to use Navigation Services
    StandardPutFile("\pSave Movie as:", "\pNewMovie.mov", &myReply); 
    if (myReply.sfGood) {
        FlattenMovieData(theMovie, flattenAddMovieToDataFork, &myReply.sfFile, FOUR_CHAR_CODE('TVOD'), smSystemScript, createMovieFileDeleteCurFile);
        myErr = GetMoviesError();
    }
 
bail:
    return(myErr);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tween utilities.
//
// Use these functions add tween entries to samples or to add attributes to tween entries.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTSndTween_AddTweenEntryToSample
// Add a tween entry to the specified sample.
//
// A tween entry defines a set of values for a single tweening operation. A tween entry is a parent atom
// whose children define the tween data type, the tween data, and additional attributes of the operation.
//
// The data specified in the theData parameter is assumed to be in big-endian format.
//
//////////
 
OSErr QTSndTween_AddTweenEntryToSample (QTAtomContainer theSample, QTAtomID theID, QTAtomType theType, void *theData, long theDataSize)
{
    OSErr               myErr = noErr;
    QTAtom              myAtom;
    
    // create an entry for this tween in the sample
    myErr = QTInsertChild(theSample, kParentAtomIsContainer, kTweenEntry, theID, 0, 0, NULL, &myAtom);
    if (myErr != noErr)
        goto bail;
    
    // set the type of this tween entry
    theType = EndianU32_NtoB(theType);
    myErr = QTInsertChild(theSample, myAtom, kTweenType, 1, 0, sizeof(theType), &theType, NULL);
    if (myErr != noErr)
        goto bail;
    
    // set the data for this tween entry
    myErr = QTInsertChild(theSample, myAtom, kTweenData, 1, 0, theDataSize, theData, NULL);
    
bail:
    return(myErr);
}
 
 
//////////
//
// QTSndTween_AddTweenEntryToInputMap
// Add a tween entry to the specified input map.
//
//////////
 
OSErr QTSndTween_AddTweenEntryToInputMap (QTAtomContainer theInputMap, long theRefIndex, long theID, OSType theType, char *theName)
{
    QTAtom              myInputAtom;
    OSErr               myErr = noErr;
    
    // add an entry to the input map
    myErr = QTInsertChild(theInputMap, kParentAtomIsContainer, kTrackModifierInput, theRefIndex, 0, 0, NULL, &myInputAtom);
    if (myErr != noErr)
        goto bail;
    
    // add two child atoms to the parent atom;
    // these atoms define the type of the modifier input and the ID of the tween entry atom
    theType = EndianU32_NtoB(theType);
    myErr = QTInsertChild(theInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(OSType), &theType, NULL);
    if (myErr != noErr)
        goto bail;
 
    theID = EndianU32_NtoB(theID);
    myErr = QTInsertChild(theInputMap, myInputAtom, kInputMapSubInputID, 1, 0, sizeof(long), &theID, NULL);
    if (myErr != noErr)
        goto bail;
        
    // set the name of the input atom
    if (theName != NULL) {
        long        myLength = 1;
        Ptr         myPtr = theName;
        UInt16      *myShort;
 
        // determine the length of the name string
        while (*myPtr++)
            myLength++;
 
        // convert the name string to the proper endian format
        myPtr = theName;
        while (*myPtr) {
            myShort = (UInt16 *)myPtr;
            *myPtr = EndianU16_NtoB(*myShort);
            myPtr = myPtr + 2;  // point to next word
        }
 
        myErr = QTInsertChild(theInputMap, myInputAtom, kTrackModifierInputName, 1, 0, myLength, theName, NULL);
    }
    
bail:
    return(myErr);
}