home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Utilities / EatAU / EatAU.p < prev    next >
Encoding:
Text File  |  1993-12-02  |  13.7 KB  |  435 lines  |  [TEXT/PJMM]

  1. unit EatAU;
  2.  
  3. (* # Copyright Department of Computer Science *)
  4. (* # University of Western Australia *)
  5. (* # Created : Quinn *)
  6. (* # Station : Eriodon *)
  7. (* # Date : Thursday, 2 December 1993 *)
  8.  
  9. interface
  10.  
  11.     uses
  12.         Components, QuickTimeComponents;
  13.  
  14.     function Main (var params: ComponentParameters; storage: Handle): ComponentResult;
  15.  
  16. implementation
  17.  
  18.     uses
  19.         FixMath, Movies;
  20.  
  21.     procedure QAssertQ (b: boolean);
  22.     (* primitive version of QLibs routine *)
  23.     begin
  24.         if not b then begin
  25.             DebugStr('Assertion failed');
  26.         end; (* if *)
  27.     end; (* QAssertQ *)
  28.  
  29.     function FSReadQ (refnum: integer; count: longint; buf: Ptr): OSErr;
  30.     (* same as FSRead but count is a value parameter *)
  31.     (* block copied from QLibs *)
  32.     begin
  33.         FSReadQ := FSRead(refnum, count, buf);
  34.     end; (* FSReadQ *)
  35.  
  36.     const
  37.         (* Hmmm, I'm stealing error codes again.  Shucks! *)
  38.         noAuFileErr = -666;
  39.         strangeEncodingErr = -667;
  40.         strangeChannelsErr = -668;
  41.         notEnoughDataErr = -669;
  42.  
  43.     const
  44.         kAUVersion = 0;                        (* the version number returned by this component *)
  45.         au_magic = '.snd';                        (* the magic long at the front of .au files *)
  46.         U_LAW = 1;                                (* the only encoding I handle, I'm basically lazy *)
  47.     type
  48.         (* This is the header for the .au file.  It's structure was basically stolen from Sun's header file. *)
  49.         auHeader = record
  50.                 magic: OSType;                    (* should be au_magic *)
  51.                 header_size: longint;            (* size of header (including comment) *)
  52.                 data_size: longint;            (* size of data after comment (might be -1 to mean rest of file) *)
  53.                 encoding: longint;                (* should be U_LAW *)
  54.                 sample_rate: longint;        (* normally 8000, we deal with any (reasonable) value *)
  55.                 channels: longint;                (* should be 1 *)
  56.                     (* followed by comment *)
  57.             end;
  58.  
  59.         (* The munge array is used to map ULAW sound samples to Mac sound samples. *)
  60.         (* I get it from the AU2L resource when the component is opened. *)
  61.         (* I stole the contents of that resource from Vic Heintz's sun_audio2mac_linear program.*)
  62.         (* There's most probably a better way of doing the conversion but this is all I needed. *)
  63.         mungeArray = packed array[byte] of byte;
  64.         mungePtr = ^mungeArray;
  65.         mungeHandle = ^mungePtr;
  66.  
  67.         (* The component's globals.  I store a copy of the munge resource in my globals (yeah I know,*)
  68.         (* that means there's one copy for each instance -- tough!).  I also store the progress procedure *)
  69.         (* and its refcon.*)
  70.         globalsRecord = record
  71.                 munge: mungeArray;
  72.                 progress_proc: ProcPtr;
  73.                 progress_refcon: longint;
  74.             end;
  75.         globalsPtr = ^globalsRecord;
  76.         globalsHandle = ^globalsPtr;
  77.  
  78.     function ReadChunk (globals: globalsHandle; refnum: integer; buffer: Handle; count: longint): OSErr;
  79.     (* Reads count bytes from refnum and puts it in buffer. Then munges it from ULAW (Sun) to linear (Mac) *)
  80.     (* using the munge array in the globals. *)
  81.         type
  82.             dataArray = packed array[0..100000] of byte;
  83.             dataPtr = ^dataArray;
  84.         var
  85.             err: OSErr;
  86.             dp: dataPtr;
  87.             ndx: longint;
  88.             munge: mungePtr;
  89.     begin
  90.         err := FSReadQ(refnum, count, buffer^);
  91.         if err = noErr then begin
  92.             dp := dataPtr(buffer^);
  93.             munge := @globals^^.munge;
  94.             for ndx := 0 to count - 1 do begin
  95.                 dp^[ndx] := munge^[dp^[ndx]];
  96.             end; (* for *)
  97.         end; (* if *)
  98.         ReadChunk := err;
  99.     end; (* ReadChunk *)
  100.  
  101.     function AUCanDo (globals: globalsHandle; selector: integer): ComponentResult;
  102.     (* Handle the Component Manager CanDo request.*)
  103.     begin
  104.         case selector of
  105.             kComponentVersionSelect..kComponentOpenSelect, {}
  106.             kMovieImportFileSelect, {}
  107.             kMovieImportSetProgressProcSelect: 
  108.                 AUCanDo := 1;
  109.             otherwise
  110.                 AUCanDo := 0;
  111.         end; (* case *)
  112.     end; (* AUCanDo *)
  113.  
  114.     function AUOpen (globals: globalsHandle; self: ComponentInstance): ComponentResult;
  115.     (* Handle the Component Manager Open request. *)
  116.         var
  117.             err: OSErr;
  118.             refnum: integer;
  119.             mungeh: mungeHandle;
  120.     begin
  121.         globals := nil;
  122.         (* create our globals *)
  123.         globals := globalsHandle(NewHandle(sizeof(globalsRecord)));
  124.         err := MemError;
  125.  
  126.         (* init them with stuff from our resource file *)
  127.         if err = noErr then begin
  128.             globals^^.progress_proc := nil;
  129.             globals^^.progress_refcon := 0;
  130.             refnum := OpenComponentResFile(Component(self));
  131.             if refnum <= 0 then begin
  132.                 err := resNotFound;
  133.             end; (* if *)
  134.         end; (* if *)
  135.         if err = noErr then begin
  136.             mungeh := mungeHandle(GetResource('AU2L', 128));
  137.             err := ResError;
  138.             if (mungeh = nil) and (err = noErr) then begin
  139.                 err := resNotFound;
  140.             end; (* if *)
  141.             if err = noErr then begin
  142.                 globals^^.munge := mungeh^^;
  143.                 ReleaseResource(Handle(mungeh));
  144.             end; (* if *)
  145.             err := CloseComponentResFile(refnum);
  146.         end; (* if *)
  147.  
  148.         (* tell the Component Manager about them *)
  149.         if err = noErr then begin
  150.             SetComponentInstanceStorage(self, Handle(globals));
  151.         end; (* if *)
  152.  
  153.         (* clean up *)
  154.         if (err <> noErr) and (globals <> nil) then begin
  155.             DisposeHandle(Handle(globals));
  156.         end; (* if *)
  157.         AUOpen := err;
  158.     end; (* AUOpen *)
  159.  
  160.     function AUClose (globals: globalsHandle; self: ComponentInstance): ComponentResult;
  161.     (* Handle the Component Manager Close request. *)
  162.     begin
  163.         if globals <> nil then begin
  164.             DisposeHandle(Handle(globals));
  165.         end; (* if *)
  166.         AUClose := noErr;
  167.     end; (* AUClose *)
  168.  
  169.     (* This inline function is used to call a Movie Toolbox style progress function. *)
  170.     function CallProgressProc (theMovie: Movie; message, whatOperation: integer; percentDone: Fixed; refcon: longint; ad: univ Ptr): OSErr;
  171.     inline
  172.         $205F, (* move.l    (a7)+,a0        ; pop proc address    *)
  173.         $4E90; (* jsr        (a0)                ; call proc                *)
  174.  
  175.     function AUSetProgressProc (globals: globalsHandle; proc: ProcPtr; refcon: longint): ComponentResult;
  176.     (* Handle the SetProgressProc request by recording the information in our globals. *)
  177.     begin
  178.         QAssertQ(globals <> nil);
  179.         globals^^.progress_proc := proc;
  180.         globals^^.progress_refcon := refcon;
  181.         AUSetProgressProc := noErr;
  182.     end; (* AUSetProgressProc *)
  183.  
  184.     function AUImportFile (globals: globalsHandle; theFile: FSSpec; theMovie: Movie; {}
  185.                                     targetTrack: Track; var usedTrack: Track; {}
  186.                                     atTime: TimeValue; var addedDuration: TimeValue; {}
  187.                                     inFlags: longint; var outFlags: longint): ComponentResult;
  188.     (* Import theFile into theMovie.*)
  189.  
  190.         var
  191.             header: auHeader;                                        (* a copy of the .au file header *)
  192.  
  193.             theMedia: Media;                                        (* the media we're adding the data to *)
  194.             sample_desc: SoundDescriptionHandle;        (* describes the sound samples we're adding *)
  195.                                                                             (* it's global to avoid having to new and dispose it every time in AddChunk *)
  196.  
  197.         function StartSoundTrack: OSErr;
  198.         (* Prepare theMovie to receive the sound data. *)
  199.             var
  200.                 err: OSErr;
  201.                 time_scale: longint;
  202.         begin
  203.             ClearMoviesStickyError;
  204.             sample_desc := nil;
  205.  
  206.             (* create a new track (if required by inFlags) *)
  207.             if band(inFlags, movieImportMustUseTrack) = 0 then begin
  208.                 targetTrack := NewMovieTrack(theMovie, 0, 0, kFullVolume);
  209.             end; (* if *)
  210.  
  211.             (* create the media and prepare it for editing *)
  212.             theMedia := NewTrackMedia(targetTrack, SoundMediaType, header.sample_rate, nil, OSType(nil));
  213.             err := BeginMediaEdits(theMedia);
  214.             if err = noErr then begin
  215.                 err := GetMoviesStickyError;
  216.                 ClearMoviesStickyError;
  217.             end; (* if *)
  218.  
  219.             (* create the sound description handle *)
  220.             if err = noErr then begin
  221.                 sample_desc := SoundDescriptionHandle(NewHandle(sizeof(SoundDescription)));
  222.                 err := MemError;
  223.             end; (* if *)
  224.  
  225.             (* call the progress proc to let it know we're beginning the conversion *)
  226.             if (err = noErr) and (globals^^.progress_proc <> nil) then begin
  227.                 err := CallProgressProc(theMovie, movieProgressOpen, progressOpImportMovie, 0, globals^^.progress_refcon, globals^^.progress_proc);
  228.             end; (* if *)
  229.             StartSoundTrack := err;
  230.         end; (* StartSoundTrack *)
  231.  
  232.         function AddChunk (globals: globalsHandle; buffer: Handle; count: longint; bytes_done, bytes_total: longint): OSErr;
  233.         (* Add count bytes from buffer into theMovie.  Use bytes_done and bytes_total to calculate progress. *)
  234.             var
  235.                 err: OSErr;
  236.                 junklong: longint;
  237.                 percent: Fixed;
  238.         begin
  239.             err := noErr;
  240.  
  241.             (* call the progress proc to let it know we're converting the movie *)
  242.             if globals^^.progress_proc <> nil then begin
  243.                 percent := FixDiv(bytes_done, bytes_total);
  244.                 err := CallProgressProc(theMovie, movieProgressUpdatePercent, progressOpImportMovie, percent, globals^^.progress_refcon, globals^^.progress_proc);
  245.             end; (* if *)
  246.  
  247.             if err = noErr then begin
  248.                 (* init the sound description handle *)
  249.                 HLock(Handle(sample_desc));
  250.                 with sample_desc^^ do begin (* unsafe, checked (locked) *)
  251.                     descSize := GetHandleSize(Handle(sample_desc));
  252.                     dataFormat := longint('raw ');
  253.                     resvd1 := 0;
  254.                     resvd2 := 0;
  255.                     dataRefIndex := 1;
  256.                     version := 0;
  257.                     revlevel := 0;
  258.                     vendor := 0;
  259.                     numChannels := 1;
  260.                     sampleSize := 8;
  261.                     compressionID := 0;
  262.                     packetSize := 0;
  263.                     sampleRate := Long2Fix(header.sample_rate);
  264.                 end; (* with *)
  265.                 HUnlock(Handle(sample_desc));
  266.  
  267.                 (* add the sound to theMedia *)
  268.                 err := AddMediaSample(theMedia, buffer, 0, count, 1, SampleDescriptionHandle(sample_desc), count, 0, junklong);
  269.             end; (* if *)
  270.             AddChunk := err;
  271.         end; (* AddChunk *)
  272.  
  273.         function EndSoundTrack: OSErr;
  274.         (* Tidy up the movie and setup the return results (usedTrack, addedDuration). *)
  275.             var
  276.                 err: OSErr;
  277.                 junk: OSErr;
  278.                 mediaDuration: TimeValue;
  279.                 tmp_time: TimeRecord;
  280.         begin
  281.              (* tidy up the movie *)
  282.             if sample_desc <> nil then begin
  283.                 DisposeHandle(Handle(sample_desc));
  284.             end; (* if *)
  285.             junk := EndMediaEdits(theMedia);
  286.             mediaDuration := GetMediaDuration(theMedia);            (* find out the length of the media *)
  287.             junk := InsertMediaIntoTrack(targetTrack, atTime, 0, mediaDuration, Long2Fix(1));
  288.  
  289.             (* calculate the added duration in movie time *)
  290.             tmp_time.value.hi := 0;
  291.             tmp_time.value.lo := mediaDuration;
  292.             tmp_time.scale := GetMediaTimeScale(theMedia);
  293.             tmp_time.base := nil;
  294.             ConvertTimeScale(tmp_time, GetMovieTimeScale(theMovie));
  295.  
  296.             (* setup the output results *)
  297.             addedDuration := tmp_time.value.lo;                            (* there goes the high long *)
  298.             usedTrack := targetTrack;
  299.  
  300.             err := noErr;
  301.             (* call the progress proc to let it know we're finished *)
  302.             if globals^^.progress_proc <> nil then begin
  303.                 err := CallProgressProc(theMovie, movieProgressClose, progressOpImportMovie, Long2Fix(100), globals^^.progress_refcon, globals^^.progress_proc);
  304.             end; (* if *)
  305.  
  306.             (* tidy up *)
  307.             if err = noErr then begin
  308.                 err := GetMoviesStickyError;
  309.             end; (* if *)
  310.             ClearMoviesStickyError;
  311.             EndSoundTrack := err;
  312.         end; (* EndSoundTrack *)
  313.  
  314.         var
  315.             refnum: integer;
  316.             err, err2: OSErr;
  317.             true_size: longint;
  318.             buffer: Handle;
  319.             buffer_size: longint;
  320.             remaining, this_time: longint;
  321.     begin
  322.         QAssertQ(globals <> nil);
  323.         refnum := 0;
  324.         buffer := nil;
  325.         buffer := NewHandle(8192);                            (* try to get a 8K buffer *)
  326.         err := MemError;
  327.         if err <> noErr then begin
  328.             buffer := NewHandle(1024);                        (* if we fail, try for a 1K buffer *)
  329.             err := MemError;
  330.         end; (* if *)
  331.         if err = noErr then begin
  332.             buffer_size := GetHandleSize(buffer);
  333.         end; (* if *)
  334.  
  335.         (* open up and check the au file *)
  336.         if err = noErr then begin
  337.             err := FSpOpenDF(theFile, fsRdPerm, refnum);
  338.         end; (* if *)
  339.         if err = noErr then begin
  340.             err := GetEOF(refnum, true_size);
  341.         end; (* if *)
  342.         if err = noErr then begin
  343.             err := FSReadQ(refnum, sizeof(header), @header);
  344.         end; (* if *)
  345.         if (err = noErr) & (header.magic <> au_magic) then begin
  346.             err := noAuFileErr;
  347.         end; (* if *)
  348.         if (err = noErr) & (header.encoding <> U_LAW) then begin
  349.             err := strangeEncodingErr;
  350.         end; (* if *)
  351.         if (err = noErr) & (header.channels <> 1) then begin
  352.             err := strangeChannelsErr;
  353.         end; (* if *)
  354.         if (err = noErr) & (header.data_size = -1) then begin
  355.             header.data_size := true_size - header.header_size;
  356.         end; (* if *)
  357.         if (err = noErr) & (header.data_size + header.header_size > true_size) then begin
  358.             err := notEnoughDataErr;
  359.         end; (* if *)
  360.         if err = noErr then begin
  361.             err := SetFPos(refnum, fsFromStart, header.header_size);            (* skip over comment *)
  362.         end; (* if *)
  363.  
  364.         (* do the work *)
  365.         if err = noErr then begin
  366.             err := StartSoundTrack;
  367.             if err = noErr then begin
  368.                 remaining := header.data_size;
  369.                 while (err = noErr) & (remaining > 0) do begin
  370.                     if remaining > buffer_size then begin
  371.                         this_time := buffer_size;
  372.                     end
  373.                     else begin
  374.                         this_time := remaining;
  375.                     end; (* if *)
  376.                     err := ReadChunk(globals, refnum, buffer, this_time);
  377.                     if err = noErr then begin
  378.                         err := AddChunk(globals, buffer, this_time, header.data_size - remaining, header.data_size);
  379.                     end; (* if *)
  380.                     if err = noErr then begin
  381.                         err := err2;
  382.                         remaining := remaining - this_time;
  383.                     end; (* if *)
  384.                 end; (* while *)
  385.                 err2 := EndSoundTrack;
  386.                 if err = noErr then begin
  387.                     err := err2;
  388.                 end; (* if *)
  389.             end; (* if *)
  390.         end; (* if *)
  391.  
  392.         (* clean up *)
  393.         if refnum <> 0 then begin
  394.             err2 := FSClose(refnum);
  395.             if err = noErr then begin
  396.                 err := err2;
  397.             end; (* if *)
  398.         end; (* if *)
  399.         if buffer <> nil then begin
  400.             DisposeHandle(buffer);
  401.         end; (* if *)
  402.         AUImportFile := err;
  403.     end; (* AUImportFile*)
  404.  
  405.     function Main (var params: ComponentParameters; storage: Handle): ComponentResult;
  406.     (* Component entry point.  It's pretty neat IMHO. *)
  407.         var
  408.             proc: ProcPtr;
  409.     begin
  410.         proc := nil;
  411.         case params.what of
  412.             (* Component Manager stuff *)
  413.             kComponentVersionSelect: 
  414.                 Main := kAUVersion;
  415.             kComponentCanDoSelect: 
  416.                 proc := @AUCanDo;
  417.             kComponentOpenSelect: 
  418.                 proc := @AUOpen;
  419.             (* Component Manager stuff *)
  420.             kComponentCloseSelect: 
  421.                 proc := @AUClose;
  422.             (* this component type stuff *)
  423.             kMovieImportFileSelect: 
  424.                 proc := @AUImportFile;
  425.             kMovieImportSetProgressProcSelect: 
  426.                 proc := @AUSetProgressProc;
  427.             otherwise
  428.                 Main := badComponentSelector;
  429.         end; (* case *)
  430.         if proc <> nil then begin
  431.             Main := CallComponentFunctionWithStorage(storage, params, proc);
  432.         end; (* if *)
  433.     end; (* Main *)
  434.  
  435. end. (* EatAU *)