FileDocCategorySizeDatePackage
AudioSampleBuilder.javaAPI DocExample7947Wed Nov 10 12:52:14 GMT 2004com.oreilly.qtjnotebook.ch07

AudioSampleBuilder.java

/*

Copyright (c) 2004, Chris Adamson

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
package com.oreilly.qtjnotebook.ch07;

import quicktime.*;
import quicktime.std.*;
import quicktime.std.movies.*;
import quicktime.std.movies.media.*;
import quicktime.io.*;
import quicktime.util.*;

import com.oreilly.qtjnotebook.ch01.QTSessionCheck;

public class AudioSampleBuilder extends Object {

    static final int SAMPLING = 44100;
    static final byte[] ONE_SECOND_SAMPLE = new byte [SAMPLING * 2];
    // static final int FREQUENCY = 1000;
    static final int FREQUENCY = 440; // A4 on piano
    // static final int FREQUENCY = 262; // middle C

    public static void main (String[] args) {
        try {
            QTSessionCheck.check();

            QTFile movFile = new QTFile (new java.io.File("buildaudio.mov"));
            Movie movie =
                Movie.createMovieFile(movFile,
                                      StdQTConstants.kMoviePlayer,
                                      StdQTConstants.createMovieFileDeleteCurFile |
                                      StdQTConstants.createMovieFileDontCreateResFile);
            
            System.out.println ("Created Movie");
            
            // create an empty audio track
            int timeScale = SAMPLING; // 44100 units per second
            Track soundTrack = movie.addTrack (0, 0, 1);
                                               
            System.out.println ("Added empty Track");
            
            // create media for this track
            Media soundMedia = new SoundMedia (soundTrack,
                                               timeScale);
            System.out.println ("Created Media");

            // add samples
            soundMedia.beginEdits();
            
            // formats at
            // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/soundformats.htm
            // "twos" is k16BitBigEndianFormat
            // int format = QTUtils.toOSType ("twos");
            int format = QTUtils.toOSType ("NONE");
            // int format = QTUtils.toOSType ("raw "); // oops, 8 bit
            
            SoundDescription soundDesc = new SoundDescription(format);
            System.out.println ("Created SoundDescription");

            soundDesc.setNumberOfChannels(1);
            soundDesc.setSampleSize(16);
            soundDesc.setSampleRate(SAMPLING);

            for (int i=0; i<5; i++) {
                // build the one-second sample
                QTHandle mediaHandle =  buildOneSecondSample (i);
                
                System.out.println ("mediaHandle length = " + 
                                    mediaHandle.getSize());
            
                soundMedia.addSample(mediaHandle, // QTHandleRef data,
                                     0, // int dataOffset,
                                     mediaHandle.getSize(), // int dataSize,
                                     1, // int durationPerSample,
                                     soundDesc, // SampleDescription sampleDesc,
                                     SAMPLING, // int numberOfSamples,
                                     0 // int sampleFlags)
                                     );
            }

            // finish editing and insert media into track
            soundMedia.endEdits();
            System.out.println ("Ended edits");
            soundTrack.insertMedia (0, // trackStart
                                    0, // mediaTime
                                    soundMedia.getDuration(), // mediaDuration
                                    1); // mediaRate
            System.out.println ("inserted media");

            // save up 
            System.out.println ("Saving...");
            OpenMovieFile omf = OpenMovieFile.asWrite (movFile);
            movie.addResource (omf,
                               StdQTConstants.movieInDataForkResID,
                               movFile.getName());
            System.out.println ("Done");

            System.exit(0);

        } catch (QTException qte) {
            qte.printStackTrace();
        }
    } // main

    /** Fill ONE_SECOND_SAMPLE with two-byte samples, according
        to some scheme (like square wave, sine wave, etc.)
        then wrap with QTHandle
     */
    public static QTHandle buildOneSecondSample (int inTime)
        throws QTException {
        // convert inTime to sample count (ie, how many samples
        // past 0 we are)
        int wavelengthInSamples = SAMPLING / FREQUENCY;
        System.out.println ("wavelengthInSamples is " + 
                            wavelengthInSamples);
        int halfWavelength = wavelengthInSamples / 2;
        int sample = inTime * SAMPLING;
        for (int i=0; i<SAMPLING*2; i+=2) {
            int offset = sample % wavelengthInSamples;
            // square wave - bytes are either 7fff or 0000
            if (offset < halfWavelength) {
                ONE_SECOND_SAMPLE[i] = (byte) 0x7f;
                ONE_SECOND_SAMPLE[i+1] = (byte) 0xff;
            } else {
                ONE_SECOND_SAMPLE[i] = (byte) 0x00;
                ONE_SECOND_SAMPLE[i+1] = (byte) 0x00;
            }
            sample ++;
        }
        return new QTHandle (ONE_SECOND_SAMPLE);
    }

    /** Fill ONE_SECOND_SAMPLE with two-byte samples, according
        to some scheme (like square wave, sine wave, etc.)
        then wrap with QTHandle

        THIS IS AN ALTERNATE VERSION THAT PRODUCES A SINE WAVE
        INSTEAD OF THE SQUARE WAVE SHOWN IN THE BOOK
    public static QTHandle buildOneSecondSample (int inTime)
        throws QTException {
        // convert inTime to sample count (ie, how many samples
        // past 0 we are)
        int wavelengthInSamples = SAMPLING / FREQUENCY;
        System.out.println ("wavelengthInSamples is " + 
                            wavelengthInSamples);
        int sample = inTime * SAMPLING;
        double twoPi = 2 * Math.PI;
        double radiansPerSample = twoPi / wavelengthInSamples;
        // each sample should be one n/th of twoPi

        for (int i=0; i<SAMPLING*2; i+=2) {
            int offset = sample % wavelengthInSamples;
            // sine wave
            double angle = offset * radiansPerSample;
            double sine = Math.sin (angle);
            // sines are -1<x<1.  we want from 0 to 0x7fff
            double heightD = (sine + 1) * (0x7fff / 2);
            // don't need to fix endianness because these are shorts, not ints
            // short height = EndianOrder.flipNativeToBigEndian16 ((short) heightD);
            short height = (short) heightD;
            // System.out.println (Integer.toString(height));
            // pack this into array as two bytes
            ONE_SECOND_SAMPLE [i] = (byte) ((height & 0xff00) >> 8);
            ONE_SECOND_SAMPLE [i+1] = (byte) (height & 0xff);
            sample ++;
        }
        return new QTHandle (ONE_SECOND_SAMPLE);
    }
    */


}