view aim-mat/tools/MakeQTMovie.m @ 4:537f939baef0 tip

various bug fixes and changed copyright message
author Stefan Bleeck <bleeck@gmail.com>
date Tue, 16 Aug 2011 14:37:17 +0100
parents 20ada0af3d7d
children
line wrap: on
line source
% support file for 'aim-mat'
%
% This external file is included as part of the 'aim-mat' distribution package
% (c) 2011, University of Southampton
% Maintained by Stefan Bleeck (bleeck@gmail.com)
% download of current version is on the soundsoftware site: 
% http://code.soundsoftware.ac.uk/projects/aimmat
% documentation and everything is on http://www.acousticscale.org


function MakeQTMovie(cmd,arg, arg2)
% function MakeQTMovie(cmd, arg, arg2)
% Create a QuickTime movie from a bunch of figures (and an optional sound).
%
% Syntax: MakeQTMovie cmd [arg]
% The following commands are supported:
%	addfigure - Add snapshot of current figure to movie
% 	addaxes - Add snapshot of current axes to movie
%	addmatrix data - Add a matrix to movie (convert to jpeg with imwrite)
%	addmatrixsc data - Add a matrix to movie (convert to jpeg with imwrite)
%		(automatically scales image data)
%	addsound data [sr] - Add sound to movie (only monaural for now)
%		(third argument is the sound's sample rate.)
%	cleanup - Remove the temporary files
%	demo - Create a demonstration movie
% 	finish - Finish movie, write out QT file
%	framerate fps - Set movies frame rate [Default is 10 fps]
%	quality # - Set JPEG quality (between 0 and 1)
% 	size [# #] - Set plot size to [width height]
% 	start filename - Start creating a movie with this name
% The start command must be called first to provide a movie name.
% The finish command must be called last to write out the movie
% data. All other commands can be called in any order.  Only one
% movie can be created at a time.
%
% This code is published as Interval Technical Report #1999-066
% The latest copy can be found at
%	http://web.interval.com/papers/1999-066/
% SB: no, unfortunatly not...
% (c) Copyright Malcolm Slaney, Interval Research, March 1999.

% This is experimental software and is being provided to Licensee
% 'AS IS.'  Although the software has been tested on Macintosh, SGI,
% Linux, and Windows machines, Interval makes no warranties relating
% to the software's performance on these or any other platforms.
%
% Disclaimer
% THIS SOFTWARE IS BEING PROVIDED TO YOU 'AS IS.'  INTERVAL MAKES
% NO EXPRESS, IMPLIED OR STATUTORY WARRANTY OF ANY KIND FOR THE
% SOFTWARE INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF
% PERFORMANCE, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
% IN NO EVENT WILL INTERVAL BE LIABLE TO LICENSEE OR ANY THIRD
% PARTY FOR ANY DAMAGES, INCLUDING LOST PROFITS OR OTHER INCIDENTAL
% OR CONSEQUENTIAL DAMAGES, EVEN IF INTERVAL HAS BEEN ADVISED OF
% THE POSSIBLITY THEREOF.
%
%   This software program is owned by Interval Research
% Corporation, but may be used, reproduced, modified and
% distributed by Licensee.  Licensee agrees that any copies of the
% software program will contain the same proprietary notices and
% warranty disclaimers which appear in this software program.

% This program uses the Matlab imwrite routine to convert each image
% frame into JPEG.  After first reserving 8 bytes for a header that points
% to the movie description, all the compressed images and the sound are
% added to the movie file.  When the 'finish' method is called then the
% first 8 bytes of the header are rewritten to indicate the size of the
% movie data, and then the movie header ('moov structure') is written
% to the output file.
%
% This routine creates files according to the QuickTime file format as
% described in the appendix of
%	"Quicktime (Inside MacIntosh)," Apple Computer Incorporated,
%	Addison-Wesley Pub Co; ISBN: 0201622017, April 1993.
% I appreciate help that I received from Lee Fyock (MathWorks) and Aaron
% Hertzmann (Interval) in debugging and testing this work.

% Changes:
% July 5, 1999 - Removed stss atom since it upset PC version of QuickTime
% November 11, 1999 - Fixed quality bug in addmatrix.  Added addmatrixsc.
% March 7, 2000 - by Jordan Rosenthal (jr@ece.gatech.edu), Added truecolor
%    capability when running in Matlab 5.3 changed some help comments, fixed
%    some bugs, vectorized some code.
% April 7, 2000 - by Malcolm.  Cleaned up axis/figure code and fixed(?) SGI
%    playback problems.  Added user data atom to give version information.
%    Fixed sound format problems.
% April 10, 2000 - by Malcolm. Fixed problem with SGI (at least) and B&W
%    addmatrix.
% July 2004 - By Stefan Bleeck. Added some lines for MATLAB 7 support

if nargin < 1
    fprintf('Syntax: MakeQTMovie cmd [arg]\n')
    fprintf('The following commands are supported:\n');
    fprintf('addfigure - Add snapshot of current figure to movie\n')
    fprintf('addaxes - Add snapshot of current axes to movie\n')
    fprintf('addmatrix data - Add a matrix to movie ');
    fprintf('(convert to jpeg)\n')
    fprintf('addmatrixsc data - Add a matrix to movie ');
    fprintf('(scale and convert to jpeg)\n')
    fprintf('addsound data - Add sound samples ');
    fprintf('(with optional rate)\n')
    fprintf('demo - Show this program in action\n');
    fprintf('finish - Finish movie, write out QT file\n');
    fprintf('framerate # - Set movie frame rate ');
    fprintf('(default is 10fps)\n');
    fprintf('quality # - Set JPEG quality (between 0 and 1)\n');
    fprintf('size [# #] - Set plot size to [width height]\n');
    fprintf('start filename - Start making a movie with ');
    fprintf('this name\n');
    return;
end

global MakeQTMovieStatus

global count
% if isempty(count)
%     count=1
% else
%     count=count+1
% end
% disp(cmd)
% MakeQTMovieStatus


MakeDefaultQTMovieStatus;		% Needed first time, ignored otherwise

switch lower(cmd)
    case 'clear'
        MakeQTMovieStatus=[];  % sb:change to force new values
        count=1;
    case {'addframe','addplot','addfigure','addaxes'}
        switch lower(cmd)
            case {'addframe','addfigure'}
                hObj = gcf;		% Add the entire figure (with all axes)
            otherwise
                hObj = gca;		% Add what's inside the current axis
        end
        frame = getframe(hObj);
        [I,map] = frame2im(frame);
        if ImageSizeChanged(size(I)) > 0
            return;
        end
        if isempty(map)
            % RGB image
            imwrite(I,MakeQTMovieStatus.imageTmp, 'jpg', 'Quality', ...
                MakeQTMovieStatus.spatialQual*100);
        else
            % Indexed image
            writejpg_map(MakeQTMovieStatus.imageTmp, I, map);
        end
        [pos, len] = AddFileToMovie;
        n = MakeQTMovieStatus.frameNumber + 1;
        MakeQTMovieStatus.frameNumber = n;
        MakeQTMovieStatus.frameStarts(n) = pos;
        MakeQTMovieStatus.frameLengths(n) = len;

        
        
        %% Allow images to be added by doing:
        %%	MakeQTMovie('addimage', '/path/to/file.jpg');
        %% This case adapted from addmatrix.  Thanks to
        %% Stephen Eglen <stephen@cogsci.ed.ac.uk> for this idea.
    case 'addimage'
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify a filename with ');
            fprintf('the image command.\n');
            return;
        end

        %% Check to see that the image is the correct size.  Do
        %% this by reading in the image and then checking its size.
        %% tim - temporary image.
        tim = imread(arg); tim_size = size(tim);

        fprintf('Image %s size %d %d\n', arg, tim_size(1), tim_size(2));
        if ImageSizeChanged(tim_size) > 0
            return;
        end
        [pos, len] = AddFileToMovie(arg);
        n = MakeQTMovieStatus.frameNumber + 1;
        MakeQTMovieStatus.frameNumber = n;
        MakeQTMovieStatus.frameStarts(n) = pos;
        MakeQTMovieStatus.frameLengths(n) = len;

    case 'addmatrix'
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify a matrix with ');
            fprintf('the addmatrix command.\n');
            return;
        end
        if ImageSizeChanged(size(arg)) > 0
            return;
        end
        % Work around a bug, at least on the
        % SGIs, which causes JPEGs to be
        % written which can't be read with the
        % SGI QT.  Turn the B&W image into a
        % color matrix.
        if ndims(arg) < 3
            arg(:,:,2) = arg;
            arg(:,:,3) = arg(:,:,1);
        end
        imwrite(arg, MakeQTMovieStatus.imageTmp, 'jpg', 'Quality', ...
            MakeQTMovieStatus.spatialQual*100);
        [pos, len] = AddFileToMovie;
        n = MakeQTMovieStatus.frameNumber + 1;
        MakeQTMovieStatus.frameNumber = n;
        MakeQTMovieStatus.frameStarts(n) = pos;
        MakeQTMovieStatus.frameLengths(n) = len;

    case 'addmatrixsc'
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify a matrix with ');
            fprintf('the addmatrix command.\n');
            return;
        end
        if ImageSizeChanged(size(arg)) > 0
            return;
        end
        arg = arg - min(min(arg));
        arg = arg / max(max(arg));
        % Work around a bug, at least on the
        % SGIs, which causes JPEGs to be
        % written which can't be read with the
        % SGI QT.  Turn the B&W image into a
        % color matrix.
        if ndims(arg) < 3
            arg(:,:,2) = arg;
            arg(:,:,3) = arg(:,:,1);
        end
        imwrite(arg, MakeQTMovieStatus.imageTmp, 'jpg', 'Quality', ...
            MakeQTMovieStatus.spatialQual*100);
        [pos, len] = AddFileToMovie;
        n = MakeQTMovieStatus.frameNumber + 1;
        MakeQTMovieStatus.frameNumber = n;
        MakeQTMovieStatus.frameStarts(n) = pos;
        MakeQTMovieStatus.frameLengths(n) = len;

    case 'addsound'
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify a sound array ');
            fprintf('with the addsound command.\n');
            return;
        end
        % Do stereo someday???
        OpenMovieFile
        MakeQTMovieStatus.soundLength = length(arg);
        arg = round(arg/max(max(abs(arg)))*32765);
        negs = find(arg<0);
        arg(negs) = arg(negs) + 65536;

        sound = mb16(arg);
        MakeQTMovieStatus.soundStart = ftell(MakeQTMovieStatus.movieFp);
        MakeQTMovieStatus.soundLen = length(sound);
        fwrite(MakeQTMovieStatus.movieFp, sound, 'uchar');
        if nargin < 3
            arg2 = 22050;
        end
        MakeQTMovieStatus.soundRate = arg2;

    case 'cleanup'
        if isstruct(MakeQTMovieStatus)
            if isfield(MakeQTMovieStatus,'movieFp') && ~isempty(MakeQTMovieStatus.movieFp)
                fclose(MakeQTMovieStatus.movieFp);
                MakeQTMovieStatus.movieFp = [];
            end
            if isfield(MakeQTMovieStatus,'imageTmp') && ~isempty(MakeQTMovieStatus.imageTmp) && isfield(MakeQTMovieStatus.imageTmp,'file') && exist(MakeQTMovieStatus.imageTmp,'file') > 0
                delete(MakeQTMovieStatus.imageTmp);
                MakeQTMovieStatus.imageTmp = [];
            end
        end
        MakeQTMovieStatus = [];

    case 'debug'
        fprintf('Current Movie Data:\n');
        fprintf('    %d frames at %d fps\n', MakeQTMovieStatus.frameNumber, ...
            MakeQTMovieStatus.frameRate);
        starts = MakeQTMovieStatus.frameStarts;
        if length(starts) > 10, starts = starts(1:10);, end;
        lens = MakeQTMovieStatus.frameLengths;
        if length(lens) > 10, lens = lens(1:10);, end;
        fprintf('         Start: %6d      Size: %6d\n', [starts; lens]);
        fprintf('    Movie Image Size: %dx%d\n', ...
            MakeQTMovieStatus.imageSize(2), ...);
            MakeQTMovieStatus.imageSize(1));
        if length(MakeQTMovieStatus.soundStart) > 0
            fprintf('    Sound: %d samples at %d Hz sampling rate ', ...
                MakeQTMovieStatus.soundLength, ...
                MakeQTMovieStatus.soundRate);
            fprintf('at %d.\n', MakeQTMovieStatus.soundStart);
        else
            fprintf('    Sound: No sound track\n');
        end
        fprintf('    Temporary files for images: %s\n', ...
            MakeQTMovieStatus.imageTmp);
        fprintf('    Final movie name: %s\n', MakeQTMovieStatus.movieName);
        fprintf('    Compression Quality: %g\n', ...
            MakeQTMovieStatus.spatialQual);


    case 'demo'
        clf
        fps = 10;
        movieLength = 10;
        sr = 22050;
        fn = 'test.mov';
        fprintf('Creating the movie %s.\n', fn);
        MakeQTMovie('start',fn);
        MakeQTMovie('size', [160 120]);
        MakeQTMovie('quality', 1.0);
        theSound = [];
        for i=1:movieLength
            plot(sin((1:100)/4+i));
            MakeQTMovie('addaxes');
            theSound = [theSound sin(440/sr*2*pi*(2^(i/12))*(1:sr/fps))];
        end
        MakeQTMovie('framerate', fps);
        MakeQTMovie('addsound', theSound, sr);
        MakeQTMovie('finish');

    case {'finish','close'}
        AddQTHeader;
        MakeQTMovie('cleanup')			% Remove temporary files

    case 'framerate'
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify the ');
            fprintf('frames/second with the framerate command.\n');
            return;
        end
        MakeQTMovieStatus.frameRate = arg;

    case 'help'
        MakeQTMovie				% To get help message.

    case 'size'
        % Size is off by one on the
        % Mac.
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify a vector with ');
            fprintf('the size command.\n');
            return;
        end
        if length(arg) ~= 2
            error('MakeQTMovie: Error, must supply 2 element size.');
        end
        oldUnits = get(gcf,'units');
        set(gcf,'units','pixels');
        cursize = get(gcf, 'position');
        cursize(3) = arg(1);
        cursize(4) = arg(2);
        set(gcf, 'position', cursize);
        set(gcf,'units',oldUnits);

    case 'start'
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify a file name ');
            fprintf('with start command.\n');
            return;
        end
        MakeQTMovie('cleanup');
        MakeDefaultQTMovieStatus;
        
        % SB: sorry, dont understadt that, but it works. try without...
        pause(0.1);
        MakeQTMovieStatus.movieName = arg;

    case 'test'
        clf
        MakeQTMovieStatus = [];
        MakeQTMovie('start','test.mov');
        MakeQTMovie('size', [320 240]);
        MakeQTMovie('quality', 1.0);
        subplot(2,2,1);
        for i=1:10
            plot(sin((1:100)/4+i));
            MakeQTMovie('addfigure');
        end
        MakeQTMovie('framerate', 10);
        MakeQTMovie('addsound', sin(1:5000), 22050);
        MakeQTMovie('debug');
        MakeQTMovie('finish');

    case 'quality'
        if nargin < 2
            fprintf('MakeQTMovie error: Need to specify a quality ');
            fprintf('(between 0-1) with the quality command.\n');
            return;
        end
        MakeQTMovieStatus.spatialQual = arg;

    otherwise
        fprintf('MakeQTMovie: Unknown method %s.\n', cmd);
end

%%%%%%%%%%%%%%%  MakeDefaultQTMovieStatus %%%%%%%%%%%%%%%%%
% Make the default movie status structure.
function MakeDefaultQTMovieStatus
global MakeQTMovieStatus
if isempty(MakeQTMovieStatus)
    MakeQTMovieStatus = struct(...
        'frameRate', 10, ...	% frames per second
        'frameStarts', [], ...  % Starting byte position
        'frameLengths', [], ...
        'timeScale', 10, ...	% How much faster does time run?
        'soundRate', 22050, ... % Sound Sample Rate
        'soundStart', [], ...	% Starting byte position
        'soundLength', 0, ...
        'soundChannels', 1, ...	% Number of channels
        'frameNumber', 0, ...
        'movieFp', [], ...		% File pointer
        'imageTmp', tempname, ...
        'movieName', 'output.mov', ...
        'imageSize', [0 0], ...
        'trackNumber', 0, ...
        'timeScaleExpansion', 100, ...
        'spatialQual', 1.0);	% Between 0.0 and 1.0
end


%%%%%%%%%%%%%%%  ImageSizeChanged %%%%%%%%%%%%%%%%%
% Check to see if the image size has changed.  This m-file can't
% deal with that, so we'll return an error.
function err = ImageSizeChanged(newsize)
global MakeQTMovieStatus

newsize = newsize(1:2);			% Don't care about RGB info, if present
oldsize = MakeQTMovieStatus.imageSize;
err = 0;

if sum(oldsize) == 0
    MakeQTMovieStatus.imageSize = newsize;
else
    if sum(newsize ~= oldsize) > 0
        fprintf('MakeQTMovie Error: New image size');
        fprintf('(%dx%d) doesn''t match old size (%dx%d)\n', ...
            newsize(1), newsize(2), oldsize(1), oldsize(2));
        fprintf('   Can''t add this image to the movie.\n');
        err = 1;
    end
end

%%%%%%%%%%%%%%%  AddFileToMovie %%%%%%%%%%%%%%%%%
% OK, we've saved out an image file.  Now add it to the end of the movie
% file we are creating.
% We'll copy the JPEG file in 16kbyte chunks to the end of the movie file.
% Keep track of the start and end byte position in the file so we can put
% the right information into the QT header.
function [pos, len] = AddFileToMovie(imageTmp)
global MakeQTMovieStatus
OpenMovieFile
if nargin < 1
    imageTmp = MakeQTMovieStatus.imageTmp;
end
fp = fopen(imageTmp, 'rb');
if fp < 0
    error('Could not reopen QT image temporary file.');
end

len = 0;
pos = ftell(MakeQTMovieStatus.movieFp);
while 1
    data = fread(fp, 1024*16, 'uchar');
    if isempty(data)
        break;
    end
    cnt = fwrite(MakeQTMovieStatus.movieFp, data, 'uchar');
    len = len + cnt;
end
fclose(fp);

%%%%%%%%%%%%%%%  AddQTHeader %%%%%%%%%%%%%%%%%
% Go back and write the atom information that allows
% QuickTime to skip the image and sound data and find
% its movie description information.
function AddQTHeader()
global MakeQTMovieStatus

pos = ftell(MakeQTMovieStatus.movieFp);
header = moov_atom;
cnt = fwrite(MakeQTMovieStatus.movieFp, header, 'uchar');
fseek(MakeQTMovieStatus.movieFp, 0, -1);
cnt = fwrite(MakeQTMovieStatus.movieFp, mb32(pos), 'uchar');
fclose(MakeQTMovieStatus.movieFp);
MakeQTMovieStatus.movieFp = [];

%%%%%%%%%%%%%%%  OpenMovieFile %%%%%%%%%%%%%%%%%
% Open a new movie file.  Write out the initial QT header.  We'll fill in
% the correct length later.
function OpenMovieFile
global MakeQTMovieStatus
if isempty(MakeQTMovieStatus.movieFp)
    fp = fopen(MakeQTMovieStatus.movieName, 'wb');
    if fp < 0
        error('Could not open QT movie output file.');
    end
    MakeQTMovieStatus.movieFp = fp;
    cnt = fwrite(fp, [mb32(0) mbstring('mdat')], 'uchar');
end

%%%%%%%%%%%%%%%  writejpg_map %%%%%%%%%%%%%%%%%
% Like the imwrite routine, but first pass the image data through the indicated
% RGB map.
function writejpg_map(name,I,map)
global MakeQTMovieStatus

[y,x] = size(I);

% Force values to be valid indexes.  This fixes a bug that occasionally
% occurs in frame2im in Matlab 5.2 which incorrectly produces values of I
% equal to zero.
I = max(1,min(I,size(map,1)));

rgb = zeros(y, x, 3);
t = zeros(y,x);
t(:) = map(I(:),1)*255; rgb(:,:,1) = t;
t(:) = map(I(:),2)*255; rgb(:,:,2) = t;
t(:) = map(I(:),3)*255; rgb(:,:,3) = t;

imwrite(uint8(rgb),name,'jpeg','Quality',MakeQTMovieStatus.spatialQual*100);

%%%%%%%%%%%%%%%  SetAtomSize %%%%%%%%%%%%%%%%%
% Fill in the size of the atom
function y=SetAtomSize(x)
y = x;
y(1:4) = mb32(length(x));

%%%%%%%%%%%%%%%  mb32 %%%%%%%%%%%%%%%%%
% Make a vector from a 32 bit integer
function y = mb32(x)
if size(x,1) > size(x,2)
    x = x';
end
x=uint32(x);
y = [bitand(bitshift(x,-24),255); ...
    bitand(bitshift(x,-16),255); ...
    bitand(bitshift(x, -8),255); ...
    bitand(x,              255)];
y = y(:)';

%%%%%%%%%%%%%%%  mb16 %%%%%%%%%%%%%%%%%
% Make a vector from a 16 bit integer
function y = mb16(x)
if size(x,1) > size(x,2)
    x = x';
end

y = [bitand(bitshift(x, -8),255); ...
    bitand(x,              255)];
y = y(:)';

%%%%%%%%%%%%%%%  mb8 %%%%%%%%%%%%%%%%%
% Make a vector from a 8 bit integer
function y = mb8(x)
if size(x,1) > size(x,2)
    x = x';
end

y = [bitand(x,              255)];
y = y(:)';

%
% The following routines all create atoms necessary
% to describe a QuickTime Movie. The basic idea is to
% fill in the necessary data, all converted to 8 bit
% characters, then fix it up later with SetAtomSize so
% that it has the correct header.  (This is easier than
% counting by hand.)

%%%%%%%%%%%%%%%  mbstring %%%%%%%%%%%%%%%%%
% Make a vector from a character string
function y = mbstring(s)
y = double(s);


%%%%%%%%%%%%%%%  dinf_atom %%%%%%%%%%%%%%%%%
function y = dinf_atom()
y = SetAtomSize([mb32(0) mbstring('dinf') dref_atom]);

%%%%%%%%%%%%%%%  dref_atom %%%%%%%%%%%%%%%%%
function y = dref_atom()
y = SetAtomSize([mb32(0) mbstring('dref') mb32(0) mb32(1) ...
    mb32(12) mbstring('alis') mb32(1)]);

%%%%%%%%%%%%%%%  edts_atom %%%%%%%%%%%%%%%%%
function y = edts_atom(add_sound_p)
global MakeQTMovieStatus
fixed1 = bitshift(1,16);			% Fixed point 1
if add_sound_p > 0
    duration = MakeQTMovieStatus.soundLength / ...
        MakeQTMovieStatus.soundRate * ...
        MakeQTMovieStatus.timeScale;
else
    duration = MakeQTMovieStatus.frameNumber / ...
        MakeQTMovieStatus.frameRate * ...
        MakeQTMovieStatus.timeScale;
end
duration = ceil(duration);

y = [mb32(0) ...				% Atom Size
    mbstring('edts') ...			% Atom Name
    SetAtomSize([mb32(0) ...			% Atom Size
    mbstring('elst') ...		% Atom Name
    mb32(0) ...			% Version/Flags
    mb32(1) ...			% Number of entries
    mb32(duration) ...		% Length of this track
    mb32(0) ...			% Time
    mb32(fixed1)])];		% Rate
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  hdlr_atom %%%%%%%%%%%%%%%%%
function y = hdlr_atom(component_type, sub_type)
if strcmp(sub_type, 'vide')
    type_string = 'Apple Video Media Handler';
elseif strcmp(sub_type, 'alis')
    type_string = 'Apple Alias Data Handler';
elseif strcmp(sub_type, 'soun')
    type_string = 'Apple Sound Media Handler';
end

y = [mb32(0) ...				% Atom Size
    mbstring('hdlr') ...			% Atom Name
    mb32(0) ...				% Version and Flags
    mbstring(component_type) ...		% Component Name
    mbstring(sub_type) ...			% Sub Type Name
    mbstring('appl') ...			% Component manufacturer
    mb32(0) ...				% Component flags
    mb32(0) ...				% Component flag mask
    mb8(length(type_string)) ...		% Type Name byte count
    mbstring(type_string)];			% Type Name
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  mdhd_atom %%%%%%%%%%%%%%%%%
function y = mdhd_atom(add_sound_p)
global MakeQTMovieStatus

if add_sound_p
    data = [mb32(MakeQTMovieStatus.soundRate)  ...
        mb32(MakeQTMovieStatus.soundLength)];
else
    data = [mb32(MakeQTMovieStatus.frameRate * ...
        MakeQTMovieStatus.timeScaleExpansion)  ...
        mb32(MakeQTMovieStatus.frameNumber * ...
        MakeQTMovieStatus.timeScaleExpansion)];
end

y = [mb32(0) mbstring('mdhd') ...		% Atom Header
    mb32(0) ...
    mb32(round(now*3600*24)) ...		% Creation time
    mb32(round(now*3600*24)) ...		% Modification time
    data ...
    mb16(0) mb16(0)];
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  mdia_atom %%%%%%%%%%%%%%%%%
function y = mdia_atom(add_sound_p)
global MakeQTMovieStatus

if add_sound_p
    hdlr = hdlr_atom('mhlr', 'soun');
else
    hdlr = hdlr_atom('mhlr', 'vide');
end

y = [mb32(0) mbstring('mdia') ...		% Atom Header
    mdhd_atom(add_sound_p) ...
    hdlr ...					% Handler Atom
    minf_atom(add_sound_p)];
y = SetAtomSize(y);


%%%%%%%%%%%%%%%  minf_atom %%%%%%%%%%%%%%%%%
function y = minf_atom(add_sound_p)
global MakeQTMovieStatus

if add_sound_p
    data = smhd_atom;
else
    data = vmhd_atom;
end

y = [mb32(0) mbstring('minf') ...		% Atom Header
    data ...
    hdlr_atom('dhlr','alis') ...
    dinf_atom ...
    stbl_atom(add_sound_p)];
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  moov_atom %%%%%%%%%%%%%%%%%
function y=moov_atom
global MakeQTMovieStatus
MakeQTMovieStatus.timeScale = MakeQTMovieStatus.frameRate * ...
    MakeQTMovieStatus.timeScaleExpansion;

if MakeQTMovieStatus.soundLength > 0
    sound = trak_atom(1);
else
    sound = [];
end

y = [mb32(0) mbstring('moov') ...
    mvhd_atom udat_atom sound trak_atom(0) ];
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  mvhd_atom %%%%%%%%%%%%%%%%%
function y=mvhd_atom
global MakeQTMovieStatus

fixed1 = bitshift(1,16);			% Fixed point 1
frac1 = bitshift(1,30);				% Fractional 1
if length(MakeQTMovieStatus.soundStart) > 0
    NumberOfTracks = 2;
else
    NumberOfTracks = 1;
end

% Need to make sure its longer
% of movie and sound lengths
MovieDuration = max(MakeQTMovieStatus.frameNumber / ...
    MakeQTMovieStatus.frameRate, ...
    MakeQTMovieStatus.soundLength / ...
    MakeQTMovieStatus.soundRate);
MovieDuration = ceil(MovieDuration * MakeQTMovieStatus.timeScale);

y = [mb32(uint8(0)) ...			% Size
    mbstring('mvhd') ...		% Movie Data
    mb32(uint8(0)) ...			% Version and Flags
    mb32(uint8(0)) ...			% Creation Time (unknown)
    mb32(uint8(0)) ...			% Modification Time (unknown)
    mb32(uint32(MakeQTMovieStatus.timeScale)) ...	% Movie's Time Scale
    mb32(MovieDuration) ...		% Movie Duration
    mb32(fixed1) ...			% Preferred Rate
    mb16(255) ...			% Preferred Volume
    mb16(uint8(0)) ...			% Fill
    mb32(uint8(0)) ...			% Fill
    mb32(uint8(0)) ...			% Fill
    mb32(fixed1) mb32(uint8(0)) mb32(uint8(0)) ...	% Transformation matrix (identity)
    mb32(uint8(0)) mb32(fixed1) mb32(uint8(0)) ...
    mb32(uint8(0)) mb32(uint8(0)) mb32(frac1) ...
    mb32(uint8(0)) ...			% Preview Time
    mb32(uint8(0)) ...			% Preview Duration
    mb32(uint8(0)) ...			% Poster Time
    mb32(uint8(0)) ...			% Selection Time
    mb32(uint8(0)) ...			% Selection Duration
    mb32(uint8(0)) ...			% Current Time
    mb32(NumberOfTracks)];		% Video and/or Sound?

y = SetAtomSize(y);

%%%%%%%%%%%%%%%  raw_image_description %%%%%%%%%%%%%%%%%
function y = raw_image_description()
global MakeQTMovieStatus

fixed1 = bitshift(1,16);			% Fixed point 1
codec = [12 'Photo - JPEG                   '];

y = [mb32(0) mbstring('jpeg') ...		% Atom Header
    mb32(0) mb16(0) mb16(0) mb16(0) mb16(1) ...
    mbstring('appl') ...
    mb32(1023) ...				% Temporal Quality (perfect)
    mb32(floor(1023*MakeQTMovieStatus.spatialQual)) ...
    mb16(MakeQTMovieStatus.imageSize(2)) ...
    mb16(MakeQTMovieStatus.imageSize(1)) ...
    mb32(fixed1 * 72) mb32(fixed1 * 72) ...
    mb32(0) ...
    mb16(0) ...
    mbstring(codec) ...
    mb16(24) mb16(65535)];
y = SetAtomSize(y);


%%%%%%%%%%%%%%%  raw_sound_description %%%%%%%%%%%%%%%%%
function y = raw_sound_description()
global MakeQTMovieStatus
y = [mb32(0) mbstring('twos') ...		% Atom Header
    mb32(0) mb16(0) mb16(0) mb16(0) mb16(0) ...
    mb32(0) ...
    mb16(MakeQTMovieStatus.soundChannels) ...
    mb16(16) ...				% 16 bits per sample
    mb16(0) mb16(0) ...
    mb32(round(MakeQTMovieStatus.soundRate*65536))];
y = SetAtomSize(y);


%%%%%%%%%%%%%%%  smhd_atom %%%%%%%%%%%%%%%%%
function y = smhd_atom()
y = SetAtomSize([mb32(0) mbstring('smhd') mb32(0) mb16(0) mb16(0)]);

%%%%%%%%%%%%%%%  stbl_atom %%%%%%%%%%%%%%%%%
% Removed the stss atom since it seems to upset the PC version of QT
% and it is empty so it doesn't add anything.
% Malcolm - July 5, 1999
function y = stbl_atom(add_sound_p)
y = [mb32(0) mbstring('stbl') ...		% Atom Header
    stsd_atom(add_sound_p) ...
    stts_atom(add_sound_p) ...
    stsc_atom(add_sound_p) ...
    stsz_atom(add_sound_p) ...
    stco_atom(add_sound_p)];
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  stco_atom %%%%%%%%%%%%%%%%%
function y = stco_atom(add_sound_p)
global MakeQTMovieStatus
if add_sound_p
    y = [mb32(0) mbstring('stco') mb32(0) mb32(1) ...
        mb32(MakeQTMovieStatus.soundStart)];
else
    y = [mb32(0) mbstring('stco') mb32(0) ...
        mb32(MakeQTMovieStatus.frameNumber) ...
        mb32(MakeQTMovieStatus.frameStarts)];
end
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  stsc_atom %%%%%%%%%%%%%%%%%
function y = stsc_atom(add_sound_p)
global MakeQTMovieStatus
if add_sound_p
    samplesperchunk = MakeQTMovieStatus.soundLength;
else
    samplesperchunk = 1;
end

y = [mb32(0) mbstring('stsc') mb32(0) mb32(1)  ...
    mb32(1) mb32(samplesperchunk) mb32(1)];
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  stsd_atom %%%%%%%%%%%%%%%%%
function y = stsd_atom(add_sound_p)
if add_sound_p
    desc = raw_sound_description;
else
    desc = raw_image_description;
end

y = [mb32(0) mbstring('stsd') mb32(0) mb32(1) desc];
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  stss_atom %%%%%%%%%%%%%%%%%
function y = stss_atom()
y = SetAtomSize([mb32(0) mbstring('stss') mb32(0) mb32(0)]);

%%%%%%%%%%%%%%%  stsz_atom %%%%%%%%%%%%%%%%%
function y = stsz_atom(add_sound_p)
global MakeQTMovieStatus
if add_sound_p
    y = [mb32(0) mbstring('stsz') mb32(0) mb32(2) ...
        mb32(MakeQTMovieStatus.soundLength)];
else
    y = [mb32(0) mbstring('stsz') mb32(0) mb32(0) ...
        mb32(MakeQTMovieStatus.frameNumber) ...
        mb32(MakeQTMovieStatus.frameLengths)];
end
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  stts_atom %%%%%%%%%%%%%%%%%
function y = stts_atom(add_sound_p)
global MakeQTMovieStatus
if add_sound_p
    count_duration = [mb32(MakeQTMovieStatus.soundLength) mb32(1)];
else
    count_duration = [mb32(MakeQTMovieStatus.frameNumber) ...
        mb32(MakeQTMovieStatus.timeScaleExpansion)];
end

y = SetAtomSize([mb32(0) mbstring('stts') mb32(0) mb32(1) count_duration]);

%%%%%%%%%%%%%%%  trak_atom %%%%%%%%%%%%%%%%%
function y = trak_atom(add_sound_p)
global MakeQTMovieStatus

y = [mb32(uint8(0)) mbstring('trak') ...		% Atom Header
    tkhd_atom(add_sound_p) ...		% Track header
    edts_atom(add_sound_p) ...		% Edit List
    mdia_atom(add_sound_p)];
y = SetAtomSize(y);

%%%%%%%%%%%%%%%  tkhd_atom %%%%%%%%%%%%%%%%%
function y = tkhd_atom(add_sound_p)
global MakeQTMovieStatus

fixed1 = bitshift(1,16);			% Fixed point 1
frac1 = bitshift(1,30);				% Fractional 1 (CHECK THIS)

if add_sound_p > 0
    duration = MakeQTMovieStatus.soundLength / ...
        MakeQTMovieStatus.soundRate * ...
        MakeQTMovieStatus.timeScale;
else
    duration = MakeQTMovieStatus.frameNumber / ...
        MakeQTMovieStatus.frameRate * ...
        MakeQTMovieStatus.timeScale;
end
duration = ceil(duration);

y = [mb32(0) mbstring('tkhd') ...	% Atom Header
    mb32(15) ...			% Version and flags
    mb32(round(now*3600*24)) ...	% Creation time
    mb32(round(now*3600*24)) ...	% Modification time
    mb32(MakeQTMovieStatus.trackNumber) ...
    mb32(0) ...
    mb32(duration) ...			% Track duration
    mb32(0) mb32(0) ...		% Offset and priority
    mb16(0) mb16(0) mb16(255) mb16(0) ...	% Layer, Group, Volume, fill
    mb32(fixed1) mb32(0) mb32(0) ...	% Transformation matrix (identity)
    mb32(0) mb32(fixed1) mb32(0) ...
    mb32(0) mb32(0) mb32(frac1)];

if add_sound_p
    y = [y mb32(0) mb32(0)];	% Zeros for sound
else
    y = [y mb32(fliplr(MakeQTMovieStatus.imageSize)*fixed1)];
end
y= SetAtomSize(y);

MakeQTMovieStatus.trackNumber = MakeQTMovieStatus.trackNumber + 1;

%%%%%%%%%%%%%%%  udat_atom %%%%%%%%%%%%%%%%%
function y = udat_atom()
atfmt = [64 double('fmt')];
atday = [64 double('day')];

VersionString = 'Matlab MakeQTMovie version April 7, 2000';

y = [mb32(0) mbstring('udta') ...
    SetAtomSize([mb32(0) atfmt mbstring(['Created ' VersionString])]) ...
    SetAtomSize([mb32(0) atday '  ' date])];
y = SetAtomSize(y);


%%%%%%%%%%%%%%%  vmhd_atom %%%%%%%%%%%%%%%%%
function y = vmhd_atom()

y = SetAtomSize([mb32(0) mbstring('vmhd') mb32(0) ...
    mb16(64) ...			% Graphics Mode
    mb16(0) mb16(0) mb16(0)]);		% Op Color