Technical: Java
Advanced Search
Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

title


Previous Section Table of Contents Next Section


Module 4- Playing sounds using QuickTime for Java


Contents

Overview

Introduction to Playing Sounds in QTJ

  1. Setting up the Project

  2. Importing Packages and Declaring Data Members

  3. Loading Sound Files from Disk

  4. Playing the Sound

Summary

Further Exploration


Overview
Module1In the first few modules, we learned how to create a window with a QTCanvas and use a compositor to display an image and a movie. In module 3, we learned how to use QuickTime's text services to render text.

In this module, we will explore the wonderful world of audio and learn how to play sounds of various formats using QuickTime for Java. On creation of the main window, we will load a sound file into memory and play it back. This module expands upon the framework that was set in place in the previous modules.

Introduction to Playing Sounds in QTJ

QuickTime for Java provides rich support for various sound file formats. Java's built-in audio support is surprisingly poor- support is limited to wav or au files and it is very difficult to control the playback rate of the sound unless you know in advance how many samples per second the sound file uses.

QuickTime for Java, on the other hand, supports a myriad of popular file formats including (but not limited to the following:

  • AIFF (Audio Interchange File Format)
  • Wave
  • AU
  • MPEG
  • MP3

Playback of audio media is reasonably straight-forward and can be integrated easily into non-QuickTime applications. In this module we will examine how to use QuickTime to play sounds.

Setting up the Project

This project is based on the previous module and assumes that you have completed it. If you would like to download a completed copy of the module 3, you may do so. We have renamed the main class to "Zoo4" and changed all instances of previous module classes to prevent confusion. Other than, we do not need to modify the main class file.

Click here to see the main source file Zoo4.java.

Importing Packages and Declaring Data Members

All of our source modification will occur in our AnimalPane source file. First, we add additional packages we need to import:

AnimalPane.java
import java.io.IOException;
import java.io.FileNotFoundException;

import quicktime.app.QTFactory;
import quicktime.app.anim.Compositor;
import quicktime.app.image.GraphicsImporterDrawer; 
import quicktime.app.image.ImagePresenter; 
import quicktime.app.image.ImageUtil;
import quicktime.app.players.MoviePresenter;
import quicktime.app.players.QTPlayer;
import quicktime.io.QTFile;
import quicktime.io.OpenMovieFile;

import quicktime.qd.QDRect;
import quicktime.qd.QDGraphics;
import quicktime.qd.QDColor; 
import quicktime.qd.QDConstants; 

import quicktime.std.image.Matrix;
import quicktime.std.StdQTConstants;
import quicktime.std.movies.Movie; 

import quicktime.QTSession;
import quicktime.QTException; 


public class AnimalPane
{
    public AnimalPane() { ... }

        ...   
  
    protected Movie makeMovie( QTFile f ) 
        throws IOException, QTException
    {
        OpenMovieFile movieFile = OpenMovieFile.asRead(f);
        Movie m = Movie.fromFile( movieFile );
        m.getTimeBase().setFlags( StdQTConstants.loopTimeBase );
        return m;	
    }
    protected QTPlayer player;
    protected Compositor compositor;
    protected MoviePresenter md;
}


After importing the appropriate package, we add a local QTPlayer data member. The QTPlayer class can be used by any application to control and playback a QuickTime movie with a standard controller. As we previously discussed in Module 2 any time-based media file can be treated as a movie by QuickTime. This includes sound file formats.

Since we just want to play a sound and don't want to give the user any playback control, we will use a QTPlayer simply to handle the playing of the sound data. Additionally, we will not be adding the QTPlayer to the compositor because we have no visual representation of our sound.

Next we will learn how to load sounds into memory for subsequent playback.

Loading Sound Files from Disk

In order to playback a sound file, we need to retrieve it from disk and load it into memory:

AnimalPane.java
    ... 

public class AnimalPane
{
   public AnimalPane()
    {
        QDRect size = new QDRect(Zoo4.WIDTH, Zoo4.HEIGHT);
        try
        {
            loadSound("data/zebra/Zebra.au");
            QDGraphics gw = new QDGraphics( size );	
            compositor = new Compositor( gw, 
                             QDColor.white, 30, 1 );

                ...

    public Compositor getCompositor( ) { return compositor; }
    protected void loadSound( String soundPath )
    {
        try
        {
            String soundLocation = QTFactory.findAbsolutePath( 
                                         soundPath ).getPath();
            player = (QTPlayer)QTFactory.makeDrawable( 
                                   "file://" + soundLocation );
        }
        catch( IOException e )
        {
            e.printStackTrace();
        }
        catch( QTException e )
        {
            e.printStackTrace();
        }
    }
protected Movie makeMovie( QTFile f ) 
throws IOException, QTException
{
                ...


In the body of our AnimalPane constructor, we call a routine called loadSound( ) that takes a string parameter representing the path to the sound file we want to load. We will implement this function shortly.

This method is placed at the beginning of the constructor so that we can load the sound immediately, before any graphics are drawn and before the compositor is created. This will allow us to play the sound asynchronously as our graphics are being drawn.

Towards the bottom of our file, we implement the loadSound( ) method. We declare it protected because we only want it accessible to this file and its subclasses. The method takes a String parameter that represents the path to the sound file to load. Now let's implement this method.

First, we get the path to the file by using the QTFactory utility class to generate a URL from a local file path. This location is simply a string. It does not need to refer to a local file. It can be the URL of a file located on the internet and served over ftp, http, or via an rtsp streaming server. This gives us great flexibility by dealing with remote and local files in the same manner. Next, we create a drawable object from the string. This may seem counter-intuitive since we are dealing with a sound and not a movie. This is because QuickTime treats sound and media files, as well as other time-based formats, in a consistent manner. By making a drawable object, we are preparing the file to be presented by the QTPlayer.

Although we are not using a controller, we could easily do so by adding our QTPlayer to our compositor. Finally, we add a try/catch block to handle any exceptions that are thrown. As always, the standard caveat applies here about error handling and how "real" applications should do better error checking and gracefully recover from problems.

Playing the Sound

Now that we have the sound loaded, it is time to play the sound:

AnimalPane.java
    ... 

public class AnimalPane
{
   public AnimalPane()
    {
        QDRect size = new QDRect(Zoo4.WIDTH, Zoo4.HEIGHT);
        try
        {
            loadSound("data/zebra/Zebra.au");
            QDGraphics gw = new QDGraphics( size );	
            compositor = new Compositor( gw, 
                             QDColor.white, 30, 1 );
                ... 

            md = new MoviePresenter( m );			
            compositor.addMember( md, 1 );			
            compositor.getTimer().setRate(1);	
            md.setRate(1);
            
            playSound();
        }
        catch ( IOException e )				
        {
            e.printStackTrace();
        }
        catch ( QTException e )
        {
            e.printStackTrace();
        }
    }
    public void playSound()	
    {
        if( player == null )
            return;

        try
        {
            player.setTime(0);
            player.startTasking();
            player.setRate(1);
        }
        catch( QTException e )
        {
            e.printStackTrace();
        }
    }
 
   public Compositor getCompositor( ) { return compositor; }
      
       ...


We add code at the end of the AnimalPane constructor to call a method we will write called PlaySound( ). This method simply plays the sound that is loaded into the QTPlayer. Let's look at the implementation a little farther down in the file (after the addText method).

We write a public routine called playSound( ) that takes no parameters. The first thing we do is check the player to make sure it is not null. If it is, we have no sound to play, so we return from the routine.

Otherwise, we create a new try block and call the setTime( ) method on the player. This will "rewind" the sound and set the playback position to the beginning of the sound file. Thus, if the QTPlayer is already playing this sound, and we call playSound( ) again, the sound will be rewound and restarted.

Then, we call the startTasking( ) of the QTPlayer inherited from the Taskable class. This creates a task that is responsible for handling the periodic data. If we do not do make this call, the sound will not play correctly.

Finally, we start the sound playing by setting the rate to 1 via the setRate( ) call. This call is the same one we used in module 2 to start the playback of the movie.

This completes the source code modifications to module 4. It's time to run your application and see the results.

If you would like to see the entire source file of either Zoo4.java, or AnimalPane.java please follow the appropriate links.

Back to top

Summary

This module demonstrated how to use QuickTime's QTPlayer class to playback audio files. We chose this methodology to play sound files because it is fairly robust, allows files to be located on disk, on the web, or streamed, and supports a variety of sound file formats including AIFF, wav, au, and MP3. Although we chose not to have a visual representation for our sound, we discussed how to give the user a controller for the sound by adding the component to the compositor.

In our next module we will learn how to control the playback of media by creating a custom controller.

Back to top

Further Exploration

The clever reader will realize quickly that the information that was presented here only scratches the surface of what could be done using QuickTime for Java. Although we will expand this example in future modules, there are few things that you could do on your own as exploration that will help you better understand how QuickTime for Java works.

Each one of these examples has a source code solution, so if you have trouble, feel free to consult it.

Experiment with one or more of the following:

  1. Change the sound file URL so that it loads from a remote source on the web
  2. Experiment with the playback rate. Make the sound play backwards or double speed.
  3. Add a controller for the sound file so that it can be controlled by the user.
  4. Make the sound playback in an endless loop.

Back to top



Previous Section Table of Contents Next Section