•QTMusic Sample Sequencer/SequencerTest.c

/*
 * file: SequencerTest.c
 *
 * started 12 January 1992 09:34
 * david van brink
 *
 * A starting point for BigEasy programs
 *
 */
 
 
/*--------------------------
    Inclusions
--------------------------*/
 
#include <QuickDraw.h>
#include <Windows.h>
#include <Memory.h>
#include <BDC.h>
//#include <Packages.h>
#include <Movies.h>
 
#include "BigEasy2.h"
#include "BigEasyUtils.h"
#include "BigEasyTextish.h"
#include "BigEasyGrafish.h"
#include "BigEasyDialogs.h"
 
#define globals
#include "SequencerTest.h"
#include "SequencerTest Filing.h"
#include "SequencerTest Realtime.h"
#include "SequencerTest Movies.h"
 
 
/*--------------------------
    Limits and Konstants
--------------------------*/
 
 
enum
    {
    mNew = 100,
    mOpen,
    mSave,
 
    mFirstDocOnly,
    mClose,
    mSaveAs,
    mTestTunePlayer,
    mTestTunePlayerFast,
    mTestTunePlayerSlave,
    mSetScale,
    mMakeMIDIMovie,
 
    mTestTuneRate,
 
    mLastDocOnly,
 
    mLast
    };
 
/*--------------------------
    Types and globals
--------------------------*/
 
#define kHeaderHeight 16
#define kDocMargin 8
#define kPip 4
 
#define kScorePartHeight (kPip*kScoreHeight + 1 + 3*kDocMargin)
 
#define SignIt(x) ( (x)?1:-1)
 
/*--------------------------
    Prototypes
--------------------------*/
static void DrawDoc(short n);
static void ClickDoc(short n,Point p,short mods);
static void KeyDoc(short n,short key,short code, short mods);
static short CloseDoc(short n);
static void ActivateDoc(short n);
static void DeactivateDoc(short n);
static void MoveDoc(short n);
static void IdleDoc(short n,Boolean front);
static void LetsQuit(void);
static void OpenAWindow(void);
static void NewDoc(void);
static void MakeWindow(void);
static void About(void);
static void InitVars(void);
 
static void PointToPip(Point p,Rect *pipRect,short *pipX,short *pipY);
static void PipToRect(short pipX,short pipY,Rect *pipRect,Rect *r);
static void SetPipBit(ScorePart *sp,short pipX, short pipY, Boolean x);
 
 
static pascal Boolean MyFilterProc(DialogPtr theDialog,
        EventRecord *theEvent,short *itemHit);
 
static void TestTuneQ1Looped(short n,short item, short ref);
static void TestTuneQ1(short n,short item, short ref);
static void TestTuneQ2(short n,short item, short ref);
static void TestTuneQ3(short n,short item, short ref);
static void TestTuneQ4(short n,short item, short ref);
 
static void TestTuneQ5(short n,short item, short ref);
static void TestTuneGaps(short n,short item, short ref);
static void TestTuneQ(short n,short item, short ref);
static void TestTune(short n,short item, short ref);
static void TestTuneSlave(short n,short item, short ref);
static void SetScale(short n,short item, short ref);
static void TilePart(ScorePart *sp,short tileSize);
 
/*--------------------------
    Computer Programs
--------------------------*/
 
 
void DrawDoc(short n)
/*
 * Draws the window.
 */
    {
    Rect r,r2;
    TDoc *d;
    short i,x,y;
    ScorePart *sp;
    short phase;
 
    d = &gDoc[n-kFirstDocWindow];
 
    GoBW();
    RGBBack(50000,50000,40000);
    EraseRect(&gBigRect);
    GoBW();
 
    MoveTo(0,kHeaderHeight - 3);
    Line(10000,0);
    MoveTo(0,kHeaderHeight - 1);
    Line(10000,0);
 
    MoveTo(10,kHeaderHeight - 6);
    TextSize(9);
    TextFont(3);
    TextFace(0);
    DrawString("\pQuickTime Score Length: ");
    if(d->qtScore)
        {
        DrawNum(GetHandleSize(d->qtScore));
        DrawString("\p bytes");
        }
 
    EraseRect(&g.timeRect);
    FrameRect(&g.timeRect);
 
    for(i = 0; i<kScoreParts; i++)
        {
        r = g.instrumentNameRect[i];
        EraseRect(&r);
        FrameRect(&r);
        MoveTo(r.left + 5,r.bottom - 3);
        DrawNum(i+1);
        DrawString("\p. ");
        DrawString(d->sr.score[i].tone.instrumentName);
 
        r2 = g.scoreRect[i];
        EraseRect(&r2);
        FrameRect(&r2);
        phase = 0;
        for(x = r2.top + kPipSize; x < r2.bottom-1; x += kPipSize)
            {
            if(phase++ % 12)
                RGBFore(50000,50000,50000);
            else
                RGBFore(20000,20000,20000);
            MoveTo(r2.left + 1,x);
            LineTo(r2.right - 2,x);
            }
        phase = 1;
        for(x = r2.left + kPipSize; x < r2.right-1; x += kPipSize)
            {
            if(phase % 16 == 0)
                RGBFore(0,0,0);
            else if(phase % 8 == 0)
                RGBFore(20000,20000,20000);
            else if(phase % 4 == 0)
                RGBFore(40000,40000,40000);
            else
                RGBFore(50000,50000,50000);
            MoveTo(x,r2.top + 1);
            LineTo(x,r2.bottom - 2);
            phase ++;
            }
 
        RGBFore(20000,20000,10000);
        PenSize(2,2);
        MoveTo(r.right,r.top + 2);
        LineTo(r.right,r2.bottom);
        LineTo(r2.left + 2,r2.bottom);
        GoBW();
 
        sp = &d->sr.score[i];
        for(x = 0; x < kScoreLength; x++)
            for(y = 0; y< kScoreHeight; y++)
                {
                if(GetPipBit(sp,x,y))
                    {
                    PipToRect(x,y,&g.scoreRect[i],&r);
                    PaintRect(&r);
                    }
                }
        }
    }
 
 
void ClickDoc(short n,Point p,short mods)
    {
    short i;
    Boolean x;
    ScorePart *sp;
    TDoc *d;
    short pipX,pipY;
    short lastPipX,lastPipY;
    short v;
    Rect r;
    ComponentResult thisError;
 
    d = &gDoc[n-kFirstDocWindow];
 
    if(PtInRect(p,&g.timeRect))
        {
        PointToPip(p,&g.timeRect,&pipX,&pipY);      /* pipX is the time */
        d->position = pipX;
        DoTheHeader(d);
        thisError = TuneInstant(d->tp,(unsigned long *)*(d->qtScore),pipX+1);
        ShowError(thisError);
        lastPipX = pipX;
        while(Button())
            {
            GetMouse(&p);
            PointToPip(p,&g.timeRect,&pipX,&pipY);      /* pipX is the time */
            if(pipX != lastPipX)
                {
                thisError = TuneInstant(d->tp,(unsigned long *)*(d->qtScore),pipX+1);
                ShowError(thisError);
                lastPipX = pipX;
                }
            }
        thisError = TuneInstant(d->tp,0,0);
        ShowError(thisError);
        }
 
    for(i = 0; i<kScoreParts; i++)
        {
        sp = &d->sr.score[i];
 
        if(PtInRect(p,&g.scoreRect[i]))
            {
            if(mods & optionKey)
                {
                PointToPip(p,&g.scoreRect[i],&pipX,&pipY);
 
                pipY = kNoteRangeHigh - pipY;
                NAPlayNote(g.na,d->noteChannel[i],pipY,64);
                while(StillDown());
                NAPlayNote(g.na,d->noteChannel[i],pipY,0);
                
                }
            else
                {
                v = p.v;
                d->changed = true;
                PointToPip(p,&g.scoreRect[i],&lastPipX,&lastPipY);
                x = !GetPipBit(sp,lastPipX,lastPipY);
                SetPipBit(sp,lastPipX,lastPipY,x);
                PipToRect(lastPipX,lastPipY,&g.scoreRect[i],&r);
                if(x)
                    PaintRect(&r);
                else
                    EraseRect(&r);
    
                do
                    {
                    GetMouse(&p);
                    if(mods & shiftKey)
                        p.v = v;
                    PointToPip(p,&g.scoreRect[i],&pipX,&pipY);
                    if(pipX != lastPipX || pipY != lastPipY)
                        {
                        if(pipX >= 0 && pipX < kScoreLength
                                && pipY >= 0 && pipY < kScoreHeight)
                            {
                            SetPipBit(sp,pipX,pipY,x);
                            PipToRect(pipX,pipY,&g.scoreRect[i],&r);
                            if(x)
                                PaintRect(&r);
                            else
                                EraseRect(&r);
                            }
                        lastPipX = pipX;
                        lastPipY = pipY;
                        }
                    } while(StillDown());
                }   /* if in score rect w/o optionkey */
            d->validQTScore = false;
            } /* if in score rect */
        else if(PtInRect(p,&g.instrumentNameRect[i]))
            {
            UnrollDoc(d);
 
            x = NAPickInstrument(g.na,MyFilterProc,"\pNew instrument for part?",&sp->tone,
                    0,0,0,0);
//          x = NAPickInstrument(g.na,MyFilterProc,"\pNew instrument for part?",&sp->tone);
 
            #ifdef separateNoteChannel
                {
                NoteRequest nr;
 
                NADisposeNoteChannel(g.na,d->noteChannel[i]);
                nr. = 2;
                nr.tone = sp->tone;         
                NoteChannel(g.na,&nr,&d->noteChannel[i]);
                }
            #else
                d->validQTScore = false;
                DoTheHeader(d);
            #endif
 
            InvalRect(&g.instrumentNameRect[i]);
            d->changed = true;
            }
        }
    FixUpMenus(d);
    }
 
 
 
 
pascal Boolean MyFilterProc(DialogPtr theDialog,
        EventRecord *theEvent,short *itemHit)
    {
    GrafPort *oldPort;
 
    GetPort(&oldPort);
    SetPort(theDialog);
 
    if(theEvent->what == updateEvt && (DialogPtr)theEvent->message != theDialog)
            HandleUpdateEvent(theEvent);
 
    SetPort(oldPort);
    return false;
    }
 
 
 
 
 
 
 
 
 
void PointToPip(Point p,Rect *pipRect,short *pipX,short *pipY)
    {
    *pipX = (p.h - pipRect->left)/kPipSize;
    *pipY = (p.v - pipRect->top)/kPipSize;
 
    if(*pipX < 0)
        *pipX = 0;
    else if (*pipX >= kScoreLength)
        *pipX = kScoreLength - 1;
 
    if(*pipY < 0)
        *pipY = 0;
    else if (*pipY >= kScoreHeight)
        *pipY = kScoreHeight - 1;
    }
 
void PipToRect(short pipX,short pipY,Rect *pipRect,Rect *r)
    {
    r->left = pipRect->left + kPipSize * pipX + 1;
    r->top = pipRect->top + kPipSize * pipY + 1;
 
    r->right = r->left + kPipSize - 1;
    r->bottom = r->top + kPipSize - 1;
    }
 
void SetPipBit(ScorePart *sp,short pipX, short pipY, Boolean x)
    {
    if(pipY >= kScoreHeight || pipX >= kScoreLength)
        Debugger(); /* bad input */
    if(x)
        sp->score[pipY][pipX/32] |= 1L<<(pipX & 31);
    else
        sp->score[pipY][pipX/32] &= ~(1L<<(pipX & 31));
    }
 
Boolean GetPipBit(ScorePart *sp,short pipX, short pipY)
    {
    if(pipY >= kScoreHeight || pipX >= kScoreLength)
        Debugger(); /* bad input */
    return ((sp->score[pipY][pipX/32]) & (1L<<(pipX & 31))) != 0;
    }
 
 
void TilePart(ScorePart *sp,short tileSize)
    {
    short i,j;
    Boolean p;
 
    for(i = tileSize; i < kScoreLength; i++)
        for(j = 0; j < kScoreHeight; j++)
            {
            p = GetPipBit(sp,i%tileSize,j);
            SetPipBit(sp,i,j,p);
            }
    }
 
 
void KeyDoc(short n,short key,short code, short mods)
    {
    TDoc *d;
    ComponentResult thisError;
 
    d = &gDoc[n-kFirstDocWindow];
 
    switch(key)
        {
        case '1':;
    tileIt:
            TilePart(&d->sr.score[0],16);
            TilePart(&d->sr.score[1],16);
            TilePart(&d->sr.score[2],16);
            TilePart(&d->sr.score[3],16);
            InvalRect(&gBigRect);
            d->validQTScore = false;
            break;
 
 
        case 'c':   /* left arrow */
            d->position = 0;
            goto snippet;
 
        case 28:    /* left arrow */
            if(mods & shiftKey)
                d->position -= 4;
            else
                d->position -= 1;
            goto snippet;
 
 
        case 29:    /* right arrow */
            if(mods & shiftKey)
                d->position += 4;
            else
                d->position += 1;
        snippet:
            if(d->position < 0 || d->position > 64)
                d->position = 0;
            DoTheHeader(d);
            thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),0x00010000,
                    d->position,d->position+4,kTuneStartNow,nil,0);
            ShowError(thisError);
            break;
        }
    }
 
short CloseDoc(short n)
/*
 * Close that window...
 */
    {
    short x;
    TDoc *d;
    Boolean cancelSave;
 
    d = &gDoc[n - kFirstDocWindow];
 
    if(d->used && d->changed)
        {
        x = EasyDialogMessage(0,d->docSpec.name,"\pSave changes before closing this document?",
                kEasyDialogSaveDiscardCancel);
        if(x == 2)
            cancelSave = true;
        else if(x == 0)
            cancelSave = SaveDoc(n,0,0);
        else
            cancelSave = false;
        }
    else
        cancelSave = false;
 
    if(!cancelSave)
        {
        short i;
 
        UninstallWindow(n);
 
    #ifdef separateNoteChannels
        for(i = 0; i<kScoreParts; i++)
            NADisposeNoteChannel(g.na,d->noteChannel[i]);
    #endif
 
        CloseComponent(d->tp);
        if(d->qtScore)
            DisposeHandle(d->qtScore);
 
        d->used = false;
        gDocCount--;
        }
 
    return cancelSave;
    }
 
void FixUpMenus(TDoc *d)
    /*
     * Make menus suitable for document d,
     * where d=nil means deactivate
     */
    {
    short moreDocs;
 
    moreDocs = SignIt(gDocCount<kDocMax);
 
    SetMenuItem(mNew,moreDocs,0,0,nil);
    SetMenuItem(mOpen,moreDocs,0,0,nil);
 
    if(d)
        {
        SetMenuItem(mSave,SignIt(d->changed || d->littleChanged),0,0,nil);
        SetMenuItemRange(mFirstDocOnly,mLastDocOnly,1,0);
        }
    else
        {
        SetMenuItem(mSave,-1,0,0,nil);
        SetMenuItemRange(mFirstDocOnly,mLastDocOnly,-1,0);
        }
    }
 
void MoveDoc(short n)
    {
    TDoc *d;
 
    d = &gDoc[n - kFirstDocWindow];
 
    d->littleChanged = true;
    FixUpMenus(d);
    }
 
void ActivateDoc(short n)
    {
    TDoc *d;
    ComponentResult thisError;
 
    d = &gDoc[n - kFirstDocWindow];
 
    thisError = TunePreroll(d->tp);
//  ShowError(thisError);
 
    FixUpMenus(&gDoc[n - kFirstDocWindow]);
    }
 
void DeactivateDoc(short n)
    {
    TDoc *d;
 
    d = &gDoc[n-kFirstDocWindow];
    UnrollDoc(d);
    FixUpMenus(nil);
    }
 
void IdleDoc(short n,Boolean front)
    {
    TDoc *d;
 
    d = &gDoc[n-kFirstDocWindow];
    }
 
 
void UnrollDoc(TDoc *d)
    {
    short j;
    ComponentResult thisError;
 
#ifdef separateNoteChannels
    for(j = 0; j<kScoreParts; j++)
        NAUnrollNoteChannel(g.na,d->noteChannel[j]);
#endif
    thisError = TuneUnroll(d->tp);
    ShowError(thisError);
    }
 
 
 
void LetsQuit(void)
    {
    short i;
    TDoc *d;
    short cancelQuit;
 
    cancelQuit = 0;
 
    for(i = 0; i<kDocMax; i++)
        {
        d = &gDoc[i];
 
        if(d->used)
            cancelQuit |= CloseDoc(i + kFirstDocWindow);
 
        if(cancelQuit)
            goto goHome;
        }
 
    gQuitApp++;
goHome:;
    }
 
void NewDoc(void)
    {
    short i;
 
    for(i = 0; i<kDocMax; i++)
        {
        if(!gDoc[i].used)
            {
            NewDocFromSaveRecord(i,nil);
            goto goHome;
            }
        }
goHome:;
    }
 
 
void NewDocFromSaveRecord(short docNumber,TSaveRecord *sr)
    {
    TDoc *d;
    Rect r;
    short i;
    ComponentResult thisError;
 
    d = &gDoc[docNumber];
 
    d->changed = false;
 
    if(sr)
        {
        gStaggerWindows = false;
        r = sr->windowRect;
 
        d->everSaved = true;
        d->sr = sr->sr;
        d->littleChanged = false;
        }
    else
        {
        gStaggerWindows = true;
        SetRect(&r,100,100,260,200);
 
        CopyPString(d->docSpec.name,"\pUntitled");
        d->everSaved = false;
        d->littleChanged = true;
 
        for(i = 0; i < kScoreParts; i++)
            {
            CopyPString(d->sr.score[i].tone.synthesizerName,"\p");
            CopyPString(d->sr.score[i].tone.instrumentName,"\p");
            d->sr.score[i].tone.instrumentNumber = 0;
            d->sr.score[i].tone.gmNumber = 1;
 
                {
                short x,y;
 
                for(x = 0; x < kScoreLengthLongs; x++)
                    for(y = 0; y < kScoreHeight; y++)
                        d->sr.score[i].score[y][x] = 0;
                }
 
            }
        }
 
    r.bottom = r.top + g.basicDocHeight;
    r.right = r.left + g.basicDocWidth;
 
    d->w = InstallWindow(docNumber + kFirstDocWindow,d->docSpec.name,&r,0,0,
            DrawDoc,ClickDoc,KeyDoc,(void *)CloseDoc,
            ActivateDoc,DeactivateDoc,IdleDoc);
    SetWindowMoveProc(docNumber + kFirstDocWindow,MoveDoc);
    d->used = true;
 
 
    #ifdef separateNoteChannel
        {
        NoteRequest nr[kScoreParts], *n;
    
        for(i = 0; i<kScoreParts; i++)
            {
            n = &nr[i];
            n->polyphony = 1;
            n->tone = d->sr.score[i].tone;
 
            thisError = NANewNoteChannel(g.na, n, &d->noteChannel[i]);
            ShowError(thisError);
            }
        }
    #endif
 
    d->tp = OpenDefaultComponent(kTunePlayerType,0);
 
    thisError = TuneSetTimeScale(d->tp,10);
    ShowError(thisError);
 
    d->qtScore = 0;
    d->validQTScore = false;
    DoTheHeader(d);
 
    gDocCount++;
goHome:;
    }
 
void About(void)
    {
    EasyDialogMessage(0,(StringPtr)0x910,
            "\pby David Van Brink",
            kEasyDialogOkay);
 
    }
 
void InitVars(void)
/*
 * Called once at startup: yes, it
 * inits the vars.
 */
    {
    TDoc *dp;
    short i;
    Rect r;
    short topBit;
 
    EnterMovies();
 
 
#if 0
    do
        {
        g.na = OpenDefaultComponent('nota',0);
        CloseComponent(g.na);
        } while (!Button());
#endif
 
    g.na = OpenDefaultComponent('nota',0);
 
 
    gDoc = (void *)NewPtrClear(kDocMax * sizeof(TDoc));
    FailNil(gDoc);
 
    for(i = 0; i<kDocMax; i++)
        {
        dp = &gDoc[i];
        dp->used = false;
        }
    gDocCount = 0;
 
    topBit = r.top = kHeaderHeight + kDocMargin;
    r.left = kDocMargin;
    for(i = 0; i<kScoreParts; i++)
        {
        r.right = r.left + kPipSize*kScoreLength + 1;
        r.bottom = r.top + 13;
        g.instrumentNameRect[i] = r;
 
        r.top = r.bottom - 1;
        r.bottom = r.top + kPipSize*kScoreHeight + 1;
        g.scoreRect[i] = r;
 
        if(i & 1)
            {
            r.top = topBit;
            r.left = r.right + kDocMargin;
            }
        else
            r.top = r.bottom + kDocMargin;
        }
 
    g.timeRect = g.scoreRect[kScoreParts-1];
    g.timeRect.top = g.timeRect.bottom + kDocMargin;
    g.timeRect.bottom = g.timeRect.top + kPipSize + 1;
    
 
    g.basicDocWidth = g.scoreRect[kScoreParts-1].right + kDocMargin;
    //g.basicDocHeight = g.scoreRect[kScoreParts-1].bottom + kDocMargin;
    g.basicDocHeight = g.timeRect.bottom + kDocMargin;
 
    }
 
void Bootstrap()
    {
/*** File Menu ***/
    InstallMenu("\pFile",nil,0);
    InstallMenuItem("\pNew/N",(void *)NewDoc,mNew);
    InstallMenuItem("\pOpenÉ/O",OpenDoc,mOpen);
    InstallMenuItem("\pClose/W",(void *)CloseDoc,-mClose);
    InstallMenuItem("\p(-",nil,0);
    InstallMenuItem("\pSave/S",(void *)SaveDoc,-mSave);
    InstallMenuItem("\pSave AsÉ",(void *)SaveAsDoc,-mSaveAs);
    InstallMenuItem("\pQuit/Q",(void *)LetsQuit,0);
 
/*** Edit Menu ***/
    InstallEditMenu(nil,nil,nil,nil,nil);
 
/*** Special Menu ***/
    InstallMenu("\pSpecial",(void *)nil,0);
    InstallMenuItem("\pMake Movie",MakeAMIDIMovie,-mMakeMIDIMovie);
    InstallMenuItem("\pMake timescaled Movie",MakeAFunkyMusicMovie,-mMakeMIDIMovie);
    InstallMenuItem("\p(-",nil,0);
 
    InstallMenuItem("\pTest Tune Looping",TestTuneQ1Looped,-mTestTunePlayer);
    InstallMenuItem("\p(-",nil,0);
 
    InstallMenuItem("\pTest Tune Queueing/1",TestTuneQ1,-mTestTunePlayer);
    InstallMenuItem("\pTest Tune Queueing/2",TestTuneQ2,-mTestTunePlayer);
    InstallMenuItem("\pTest Tune Queueing/3",TestTuneQ3,-mTestTunePlayer);
    InstallMenuItem("\pTest Tune Queueing/4",TestTuneQ4,-mTestTunePlayer);
    InstallMenuItem("\pTest Tune Rate/5",TestTuneQ5,-mTestTunePlayer);
    InstallMenuItem("\pTest Tune Gaps/6",TestTuneGaps,-mTestTunePlayer);
 
 
    InstallMenuItem("\p(-",nil,0);
 
    InstallMenuItem("\pTest Fast Queueing/Á",TestTuneQ1,-mTestTunePlayerFast);
    InstallMenuItem("\pTest Fast Queueing/ª",TestTuneQ2,-mTestTunePlayerFast);
    InstallMenuItem("\pTest Fast Queueing/£",TestTuneQ3,-mTestTunePlayerFast);
    InstallMenuItem("\pTest Fast Queueing/¢",TestTuneQ4,-mTestTunePlayerFast);
 
    InstallMenuItem("\p(-",nil,0);
 
    InstallMenuItem("\pTest Tune Queueing/7",TestTuneQ,-mTestTunePlayer);
    InstallMenuItem("\pTest Tune/8",TestTune,-mTestTunePlayer);
    InstallMenuItem("\pTest Tune With Slave Time/9",TestTuneSlave,-mTestTunePlayerSlave);
 
    InstallMenuItem("\pSet ScaleÉ",SetScale,-mSetScale);
 
 
 
        {
        Str63 s;
 
        CopyPString(s,"\pAbout ");
        ConcatenatePStrings(s,(StringPtr)0x910);
        ConcatenatePStrings(s,"\pÉ");
        SetAbout(s,0,About);
        }
 
    SetMasterOpenDocProc(OpenDocSpec);
    SetMasterQuitAppProc(LetsQuit);
 
    InitVars();
    }
 
void DoTheHeader(TDoc *d)
    {
    short i;
    ComponentResult thisError;
    Handle header;
 
    UnrollDoc(d);
 
    if(!d->validQTScore)
        {
        ScoreToQTScore(d);
        header = BuildMusicHeader(d);
    
        thisError = TuneSetHeader(d->tp,(unsigned long *)*header);
        ShowError(thisError);
 
        DisposeHandle(header);
 
 
        for(i = 0; i < kScoreParts; i++)
            {
            thisError = TuneGetIndexedNoteChannel(d->tp, i + 1, &d->noteChannel[i]);
            ShowError(thisError);
            }
 
 
        }
    }
 
 
 
void TestTune(short n,short item, short ref)
    {
    TDoc *d;
    ComponentResult thisError;
 
    d = &gDoc[n - kFirstDocWindow];
 
    DoTheHeader(d);
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),0x00010000,0,1000000,kTuneStartNow,nil,0);
    ShowError(thisError);
    }
 
void TestTuneQ(short n,short item, short ref)
    {
    TDoc *d;
    ComponentResult thisError;
 
    d = &gDoc[n - kFirstDocWindow];
 
    if(!d->validQTScore)
        return;
 
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),0x00010000,0,1000000,kTuneExcludeEdgeNotes,nil,0);
    ShowError(thisError);
    }
 
void TestTuneQCommon(TDoc *d,Fixed rate,short ref,long start,long stop);
void TestTuneQCommon(TDoc *d,Fixed rate,short ref,long start,long stop)
    {
    ComponentResult thisError;
 
    if(!d->validQTScore)
        return;
 
    if(rate == 0)
        rate = ref == mTestTunePlayerFast ? 2*0x00010000 : 0x00010000;
 
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),rate,start,stop,kTuneExcludeEdgeNotes,nil,0);
    ShowError(thisError);
    }
 
void TestTuneQ1Looped(short n,short item, short ref)
    {
    ComponentResult thisError;
    TDoc *d;
 
    d = &gDoc[n - kFirstDocWindow];
 
    if(!d->validQTScore)
        return;
 
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),0x00010000,
            0,16,16+kTuneExcludeEdgeNotes,nil,0);
    ShowError(thisError);
    }
 
void TestTuneQ1(short n,short item, short ref)
    {
    TDoc *d;
 
    d = &gDoc[n - kFirstDocWindow];
    TestTuneQCommon(d,0,ref,0,16);
    }
 
void TestTuneQ2(short n,short item, short ref)
    {
    TDoc *d;
 
    d = &gDoc[n - kFirstDocWindow];
    TestTuneQCommon(d,0,ref,16,32);
    }
 
void TestTuneQ3(short n,short item, short ref)
    {
    TDoc *d;
 
    d = &gDoc[n - kFirstDocWindow];
    TestTuneQCommon(d,0,ref,32,48);
    }
 
void TestTuneQ4(short n,short item, short ref)
    {
    TDoc *d;
 
    d = &gDoc[n - kFirstDocWindow];
    TestTuneQCommon(d,0,ref,48,64);
    }
 
static void TestTuneQ5(short n,short item, short ref)
    {
    TDoc *d;
    ComponentResult thisError;
 
    d = &gDoc[n - kFirstDocWindow];
 
    if(!d->validQTScore)
        return;
 
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),0x00010000,0,16,kTuneExcludeEdgeNotes,nil,0);
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),2*0x00010000,16,32,kTuneExcludeEdgeNotes,nil,0);
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),3*0x00010000,32,48,kTuneExcludeEdgeNotes,nil,0);
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),0x00010000,48,64,kTuneExcludeEdgeNotes,nil,0);
    ShowError(thisError);
    }
 
 
static void TestTuneGaps(short n,short item, short ref)
    {
    TDoc *d;
    ComponentResult thisError;
 
    d = &gDoc[n - kFirstDocWindow];
 
    if(!d->validQTScore)
        return;
 
    thisError = TuneQueue(d->tp,
            0,0x00010000,0,16,kTuneExcludeEdgeNotes,nil,0);
    }
 
 
 
 
 
 
 
 
void IdleSlave(long refcon);
void IdleSlave(long refcon)
    {
    TimeBase tb;
    Rect r;
 
    tb = (TimeBase)refcon;
 
    SetRect(&r,0,0,300,35);
 
    MoveTo(30,30);
    EraseRect(&r);
    DrawNum(GetTimeBaseTime(tb,1000,nil));
    }
 
 
void TestTuneSlave(short n,short item, short ref)
    {
    TDoc *d;
    TimeBase myTB,theirTB;
    ComponentInstance clock;
    Component clockComponent;
    ComponentDescription cd;
    Rect r;
    ComponentResult thisError;
 
    d = &gDoc[n - kFirstDocWindow];
 
    DoTheHeader(d);
 
    thisError = TuneGetTimeBase(d->tp,&theirTB);
    ShowError(thisError);
    clock = GetTimeBaseMasterClock(theirTB);
 
    GetComponentInfo((Component)clock,&cd,0,0,0);
    clockComponent = FindNextComponent(0,&cd);
 
    myTB = NewTimeBase();
    SetTimeBaseRate(myTB,0x00000000);
    SetTimeBaseMasterTimeBase(theirTB,myTB,0);
 
    thisError = TuneQueue(d->tp,(unsigned long *)*(d->qtScore),0x00010000,0,1000000,kTuneStartNow,nil,0);
    ShowError(thisError);
 
    EasyDialogMessage(3,
            "\pPlay Tune",
            "\pStart the tune?",
            kEasyDialogOkay);
 
    SetTimeBaseRate(myTB,0x00030000);
 
    SetRect(&r,40,100,240,200);
    EasyDialog(&r,nil,nil,nil,IdleSlave,kEasyDialogOkay,(long)theirTB);
 
    SetTimeBaseMasterClock(theirTB,clockComponent,nil);
    DisposeTimeBase(myTB);
    }
 
static void SetScale(short n,short item, short ref)
    {
    TDoc *d;
    ComponentResult thisError;
    TimeScale ts;
    Boolean x;
 
    d = &gDoc[n - kFirstDocWindow];
 
    TuneGetTimeScale(d->tp,&ts);
 
    x = EasyDialogGetNumber("\pTune Scale",
            "\pEnter the number of beats per second:",
            (long *)&ts);
 
    thisError = TuneSetTimeScale(d->tp,ts);
    }
 
 
void Hatstrap()
/*
  * clean up
  */
    {
    }
 
 
void ShowError(long error)
    {
    Str255 s1,s2,s3;
 
    if(error < 0)
        {
        AnyBaseToPString(error,16,8,s1);
        NumToString((short)(error & 0x0000FFFF),s2);
        CopyPString(s3,"\pComponent result: ");
        ConcatenatePStrings(s3,s1);
        ConcatenatePStrings(s3,"\p. Error word: ");
        ConcatenatePStrings(s3,s2);
        ConcatenatePStrings(s3,"\p.");
        EasyDialogMessage(0,"\pError",s3,kEasyDialogOkay);
        }
    }