Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

/QTSndTween.c

View Source Code:

Download Sample (“qtsndtween.zip”, 4.0K)
Download Sample (“qtsndtween.dmg”, 62.0K)



//
//  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);
}



Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.