TimeCode Samples/AddTimeCodeƒ/AddTC.c

/*
  File:         AddTC.c
  Contains:     Time Code Functions.
  Written by:   QT Engineering
  Copyright:    © 1991-1994 by Apple Computer, Inc., all rights reserved.
  Change History (most recent first):
  <1>       12/7/94     khs     changed the format of the file to the new look and feel
  To Do:
*/
 
 
// INCLUDES
#include <errors.h>
#include <memory.h>
#include <qdoffscreen.h>
#include <palettes.h>
#include <resources.h>
#include <toolutils.h>
#include <osutils.h>
#include <files.h>
#include <desk.h>
#include <events.h>
#include <osevents.h>
#include <menus.h>
#include <timer.h>
#include <gestaltequ.h>
#include <string.h>
#include <strings.h>
#include <fonts.h>
#include <dialogs.h>
#include <QuickDrawText.h>
 
#include <stdio.h>
 
#include <Components.h>
#include <Movies.h>
#include <QuickTimeComponents.h>
 
 
// GLOBALS
Str255 tcSrcName;
Boolean tcDisplayTimeCode = false;
Boolean tcDisplayBelowVideo = true;
TimeScale tcTimeScale = 2997;
TimeValue tcFrameDur = 100;
long tcNumFrames = 30;
Boolean tcDropFrameVal = true;
Boolean tc24Hour = true;
Boolean tcNegOK = false;
Boolean tcIsNeg = false;
long tcHours = 0;
long tcMinutes = 0;
long tcSeconds = 0;
long tcFrames = 0;
long tcCounterVal = 0;
Boolean tcUseTimeCode = true;
 
 
// FUNCTION PROTOTYPES
extern OSErr    GetMovie(Movie *theOutMovie, short *refnum, short *resid);
extern OSErr    InitEveryBody(void);
extern void     main(void);
extern void     AddTimeCode(Movie theMovie);
extern void     DeleteTimeCodes(Movie theMovie);
extern Boolean  GetTimeCodeOptions(void);
extern void     setDialogTextNumber(DialogPtr d, short itemNumber, long number);
extern void     setDialogTextString(DialogPtr d, short itemNumber, StringPtr str);
extern Boolean  validateDialogLong(DialogPtr d, short itemNumber, long *result);
ControlHandle getDItemHandle(DialogPtr d, short itemNumber);
ControlHandle getDItemRect(DialogPtr d, short itemNumber, Rect *r);
 
 
// FUNCTIONS
OSErr GetMovie(Movie *theOutMovie, short *refnum, short *resid)
{
    SFTypeList  typeList;
    StandardFileReply reply;
    OSErr       result = noErr;
    Movie       theMovie = nil;
    
    typeList[0] = 'MooV';
    StandardGetFile(nil, 1, typeList, &reply);
    if (!reply.sfGood) goto bail;
 
    result = OpenMovieFile(&reply.sfFile, refnum, 0);
    if (result != noErr) goto bail;
    *resid = 0;
    
    result = NewMovieFromFile(&theMovie,*refnum, resid, nil,0, nil);
    if (result != noErr) goto bail;
 
bail:
    if (result) {
        DisposeMovie(theMovie);
        if (*refnum) CloseMovieFile(*refnum);
        *theOutMovie = nil;
        }
    else
        *theOutMovie = theMovie;
    return result;
}
 
 
OSErr InitEveryBody(void)
{
    InitGraf(&qd.thePort);
    InitFonts();
    FlushEvents(0xffff,0);
    InitWindows();
    InitMenus();
    InitDialogs(0);
    TEInit();
    InitCursor();
 
 
    return EnterMovies();
}
 
 
void main( void )
{
    OSErr err;
    Movie theMovie;
    short movieResRef;
    short movieResID;
    
    MaxApplZone();
    MoreMasters();
    MoreMasters();
 
    err = InitEveryBody();
    if (err) return;
    
    err = GetMovie(&theMovie, &movieResRef, &movieResID );
    
    if (!GetTimeCodeOptions())
        return;
    
    // delete any existing TimeCode tracks
    DeleteTimeCodes( theMovie );
    AddTimeCode( theMovie );    
 
    UpdateMovieResource( theMovie, movieResRef, movieResID, nil );
    CloseMovieFile( movieResRef );
 
}
 
 
#ifndef fieldOffset
    #define fieldOffset(type, field) ((short) &((type *) 0)->field)
#endif
 
void DeleteTimeCodes( Movie theMovie )
{
    Track theTrack;
    
    theTrack = GetMovieIndTrackType(theMovie, 1, TimeCodeMediaType, movieTrackMediaType);
    while (theTrack) {
        DisposeMovieTrack( theTrack );
        theTrack = GetMovieIndTrackType(theMovie, 1, TimeCodeMediaType, movieTrackMediaType);
        }
 
}
 
void AddTimeCode( Movie theMovie )
{
    OSErr err;
    Track theTrack;
    Media theMedia;
    MediaHandler mh;
    long i;
    TimeCodeDef tcdef;
    TimeCodeRecord tcrec;
    UserData srcRef;
    Str63 tcStr;
    TimeValue movieDur;
    Rect curMovieBox;
    MatrixRecord mr;
    Fixed fwidth;
    Fixed fheight;
    long startingTrackCount;
    long flags;
    
    // calculate track height based on text
    
    TCTextOptions textOptions;
    FontInfo fontInfo;
    
    startingTrackCount = GetMovieTrackCount( theMovie );
    
    GetMovieBox( theMovie, &curMovieBox );
    OffsetRect( &curMovieBox, -curMovieBox.left, -curMovieBox.top );
    
    theTrack = NewMovieTrack( theMovie, 20, 20, 0);     // random track height
    theMedia = NewTrackMedia( theTrack, TimeCodeMediaType, GetMovieTimeScale(theMovie), nil, 0 );
    mh = GetMediaHandler( theMedia );
    
    // get display options to calc box height
    TCGetDisplayOptions( mh, &textOptions );
    
    if (!tcUseTimeCode) {
        flags = tcCounter;
        }
    else {
        flags = 0;
        if (tcDropFrameVal) flags |= tcDropFrame;
        if (tcIsNeg) flags |= tcNegTimesOK;
        if (tc24Hour) flags |= tc24HourMax;
        }
    tcdef.flags = flags;
    tcdef.fTimeScale = tcTimeScale;
    tcdef.frameDuration = tcFrameDur;
    tcdef.numFrames = tcNumFrames;
 
    if (!tcUseTimeCode) {
        tcrec.c.counter = tcCounterVal;
        }
    else {
        tcrec.t.hours = tcHours;
        tcrec.t.minutes = tcMinutes;        // negative flag is here
        tcrec.t.seconds = tcSeconds;
        tcrec.t.frames = tcFrames;
        if (tcIsNeg) tcrec.t.minutes |= tctNegFlag;
        }
 
    // use starting time to figure out dimensions of track
    
    err = TCTimeCodeToString( mh, &tcdef, &tcrec, tcStr);
    TextFont( textOptions.txFont );
    TextFace( textOptions.txFace );
    TextSize( textOptions.txSize );
    GetFontInfo(&fontInfo);
    
    fheight = (long)(fontInfo.ascent + fontInfo.descent + 2)<<16;
    fwidth = (long)(StringWidth(tcStr) + 4)<<16;
    
    GetTrackMatrix( theTrack, &mr );
    if (tcDisplayBelowVideo) {
        fwidth = (long)(curMovieBox.right - curMovieBox.left) << 16;
        SetTrackDimensions( theTrack, fwidth, fheight );
        TranslateMatrix( &mr, 0, ((long)curMovieBox.bottom) << 16 );
        }
    else {
        SetTrackDimensions( theTrack, fwidth, fheight );
        TranslateMatrix( &mr, (Fixed)(curMovieBox.left + ((curMovieBox.right - curMovieBox.left - (fwidth>>16))>>1))<<16, 
                (Fixed)(curMovieBox.bottom - (((fheight + (fheight>>1)) >> 16))) << 16 );
        }
    SetTrackMatrix( theTrack, &mr );
    
    SetTrackEnabled( theTrack, tcDisplayTimeCode?true:false );
    TCSetTimeCodeFlags( mh, tcDisplayTimeCode?tcdfShowTimeCode:0, tcdfShowTimeCode );
    
    err = BeginMediaEdits( theMedia );
    
    {
    TimeCodeDescriptionHandle tcdH;
    long **frameH;
    long size;
    Handle nameH;
    
    size = fieldOffset(TimeCodeDescription, srcRef);
    tcdH = (TimeCodeDescriptionHandle) NewHandleClear( size );
    (**tcdH).descSize = size;
    (**tcdH).dataFormat = TimeCodeMediaType;
    (**tcdH).timeCodeDef = tcdef;
    
    err = NewUserData( &srcRef );
    err = PtrToHand( &tcSrcName[1], &nameH, tcSrcName[0] );
    err = AddUserDataText( srcRef, nameH, 'name', 1, langEnglish );
    TCSetSourceRef( mh, tcdH, srcRef );
    
    frameH = (long**)NewHandle( sizeof(long) );
    
    err = TCTimeCodeToFrameNumber( mh, 
                &(**tcdH).timeCodeDef, &tcrec, *frameH );
    
    movieDur = GetMovieDuration(theMovie);
    // Since we created the track with the same timescale as the movie,
    // we don't need to convert the duration
    
    err = AddMediaSample( theMedia, (Handle)frameH, 0, GetHandleSize((Handle)frameH),
        movieDur,
        (SampleDescriptionHandle)tcdH, 
        1, 0, 0 );
        
    DisposeHandle( (Handle) tcdH );
    DisposeHandle( (Handle) frameH );
    DisposeHandle( nameH );
    DisposeUserData( srcRef );
    }
    
    err = EndMediaEdits( theMedia );    
 
    err = InsertMediaIntoTrack( theTrack, 0, 0, movieDur, fixed1 );
    
    // let's record that this is timecode for all the current tracks
    for (i = 1; i <= startingTrackCount; i++) {
        err = AddTrackReference(GetMovieIndTrack(theMovie, i ), theTrack, TimeCodeMediaType, nil);
    }
}
 
#define diDialog 128
 
#define diSrcName 4
#define diDisplayTimeCode 5
#define diTimeScale 7
#define diFrameDur 9
#define diNumFrames 11
 
#define diUseTC 12
#define diUseCounter 13
 
#define diDropFrame 14
#define di24Hour (diDropFrame + 1)
#define diNegOK (di24Hour + 1)
#define diIsNeg (diNegOK + 2)
#define diHours (diIsNeg + 1)
#define diMinutes (diHours + 1)
#define diSeconds (diMinutes + 1)
#define diFrames (diSeconds + 1)
 
#define diCounter 24
#define diBelowVideo 25
 
Boolean GetTimeCodeOptions(void)
{
    DialogPtr optionsDialog = nil;
    short itemHit = cancel;
    ControlHandle curCtl;
    Rect negRect;
    GrafPtr curPort;
    
    GetPort(&curPort);
    
    optionsDialog = GetNewDialog( diDialog, nil, (WindowPtr)-1L );
    if (!optionsDialog) goto bail;
    
    SetDialogDefaultItem(optionsDialog, 1);
    SetDialogCancelItem(optionsDialog, 2);
 
    curCtl = getDItemHandle( optionsDialog, diDisplayTimeCode );
    SetCtlValue(curCtl,tcDisplayTimeCode);
    curCtl = getDItemHandle( optionsDialog, diBelowVideo );
    SetCtlValue(curCtl,tcDisplayBelowVideo);
    curCtl = getDItemHandle( optionsDialog, diDropFrame );
    SetCtlValue(curCtl,tcDropFrameVal);
    curCtl = getDItemHandle( optionsDialog, di24Hour );
    SetCtlValue(curCtl,tc24Hour);
    curCtl = getDItemHandle( optionsDialog, diNegOK );
    SetCtlValue(curCtl,tcNegOK);
    curCtl = getDItemRect( optionsDialog, diIsNeg, &negRect );
    SetPort(optionsDialog);
    MoveTo(negRect.left+2, negRect.top+17);
    FrameRect(&negRect);
    TextSize(20);
    if (tcIsNeg) 
        DrawString("\p-");
    else
        DrawString("\p+");
    TextSize(12);
 
    curCtl = getDItemHandle( optionsDialog, diUseTC );
    SetCtlValue(curCtl,tcUseTimeCode?1:0);
    curCtl = getDItemHandle( optionsDialog, diUseCounter );
    SetCtlValue(curCtl,tcUseTimeCode?0:1);
 
    setDialogTextNumber(optionsDialog, diTimeScale, tcTimeScale );
    setDialogTextNumber(optionsDialog, diFrameDur, tcFrameDur );
    setDialogTextNumber(optionsDialog, diNumFrames, tcNumFrames );
    setDialogTextNumber(optionsDialog, diHours, tcHours );
    setDialogTextNumber(optionsDialog, diMinutes, tcMinutes );
    setDialogTextNumber(optionsDialog, diSeconds, tcSeconds );
    setDialogTextNumber(optionsDialog, diFrames, tcFrames );
 
    setDialogTextNumber(optionsDialog, diCounter, tcCounterVal );
    
    setDialogTextString(optionsDialog, diSrcName, tcSrcName);
 
noGood:
    do {
    
        ModalDialog( nil, &itemHit );
        switch (itemHit) {
            case diDisplayTimeCode:
                curCtl = getDItemHandle( optionsDialog, diDisplayTimeCode );
                SetCtlValue(curCtl,!GetCtlValue(curCtl));
                break;
            case diBelowVideo:
                curCtl = getDItemHandle( optionsDialog, diBelowVideo );
                SetCtlValue(curCtl,!GetCtlValue(curCtl));
                break;
            case diDropFrame:
                curCtl = getDItemHandle( optionsDialog, diDropFrame );
                SetCtlValue(curCtl,!GetCtlValue(curCtl));
                break;
            case di24Hour:
                curCtl = getDItemHandle( optionsDialog, di24Hour );
                SetCtlValue(curCtl,!GetCtlValue(curCtl));
                break;
            case diNegOK:
                curCtl = getDItemHandle( optionsDialog, diNegOK );
                SetCtlValue(curCtl,!GetCtlValue(curCtl));
                break;
            case diUseTC:
                curCtl = getDItemHandle( optionsDialog, diUseTC );
                SetCtlValue(curCtl,1);
                curCtl = getDItemHandle( optionsDialog, diUseCounter );
                SetCtlValue(curCtl,0);
                break;
            case diUseCounter:
                curCtl = getDItemHandle( optionsDialog, diUseCounter );
                SetCtlValue(curCtl,1);
                curCtl = getDItemHandle( optionsDialog, diUseTC );
                SetCtlValue(curCtl,0);
                break;
            case diIsNeg:
                tcIsNeg = !tcIsNeg;
                SetPort(optionsDialog);
    MoveTo(negRect.left+2, negRect.top+17);
                FrameRect(&negRect);
                InsetRect(&negRect,1,1);
                EraseRect(&negRect);
                InsetRect(&negRect,-1,-1);
                TextSize(20);
                if (tcIsNeg) 
                    DrawString("\p-");
                else
                    DrawString("\p+");
                TextSize(12);
                break;
            }
    } while ((itemHit != ok) && (itemHit != cancel));
    
    if (itemHit == ok) {
        curCtl = getDItemHandle(optionsDialog, diSrcName);
        GetIText((Handle)curCtl, tcSrcName);
    
        curCtl = getDItemHandle( optionsDialog, diDisplayTimeCode );
        tcDisplayTimeCode = GetCtlValue(curCtl);
        curCtl = getDItemHandle( optionsDialog, diBelowVideo );
        tcDisplayBelowVideo = GetCtlValue(curCtl);
        curCtl = getDItemHandle( optionsDialog, diDropFrame );
        tcDropFrameVal = GetCtlValue(curCtl);
        curCtl = getDItemHandle( optionsDialog, di24Hour );
        tc24Hour = GetCtlValue(curCtl);
        curCtl = getDItemHandle( optionsDialog, diNegOK );
        tcNegOK = GetCtlValue(curCtl);
        curCtl = getDItemHandle( optionsDialog, diUseTC );
        tcUseTimeCode = (GetCtlValue(curCtl) != 0);
 
        if (!validateDialogLong(optionsDialog, diTimeScale, &tcTimeScale))
            goto noGood;
        if (!validateDialogLong(optionsDialog, diFrameDur, &tcFrameDur))
            goto noGood;
        if (!validateDialogLong(optionsDialog, diNumFrames, &tcNumFrames))
            goto noGood;
        if (!validateDialogLong(optionsDialog, diHours, &tcHours))
            goto noGood;
        if (!validateDialogLong(optionsDialog, diMinutes, &tcMinutes))
            goto noGood;
        if (!validateDialogLong(optionsDialog, diSeconds, &tcSeconds))
            goto noGood;
        if (!validateDialogLong(optionsDialog, diFrames, &tcFrames))
            goto noGood;
        if (!validateDialogLong(optionsDialog, diCounter, &tcCounterVal))
            goto noGood;
        }
    
    DisposDialog(optionsDialog);
bail:
    SetPort(curPort);
    return (itemHit == ok);
}
 
void setDialogTextNumber(DialogPtr d, short itemNumber, long number)
{
    Str255 theText;
 
    NumToString(number, theText);
    SetIText((Handle)getDItemHandle(d, itemNumber), theText);
    SelIText(d, itemNumber, 0, 32767);
}
 
 
void setDialogTextString(DialogPtr d, short itemNumber, StringPtr str)
{
    SetIText((Handle)getDItemHandle(d, itemNumber), str);
    SelIText(d, itemNumber, 0, 32767);
}
 
Boolean validateDialogLong(DialogPtr d, short itemNumber, long *result)
{
    Str255 theText;
    ControlHandle ch;
    Boolean digitFound;
    short i;
 
    ch = getDItemHandle(d, itemNumber);
 
    GetIText((Handle)ch, theText);
 
    digitFound = false;
    for (i = 1; i < theText[0]; i++) {
        if (theText[i] >= '0' && theText[i] <= '9') 
            digitFound = true;
        else if (digitFound) {
            theText[0] = i-1;
            break;
        }
        else if (theText[i] != ' ') {
            SelIText(d, itemNumber, 0, 32767);
            SysBeep(1);
            return false;
        }
    }
 
    StringToNum( theText, result );
 
    return true;
}
 
ControlHandle getDItemHandle(DialogPtr d, short itemNumber)
{
    short kind;
    ControlHandle ch;
    Rect r;
 
    GetDItem(d, itemNumber, &kind, (Handle *)&ch, &r);
 
    return(ch);
}
 
ControlHandle getDItemRect(DialogPtr d, short itemNumber, Rect *r)
{
    short kind;
    ControlHandle ch;
 
    GetDItem(d, itemNumber, &kind, (Handle *)&ch, r);
 
    return(ch);
}