/*
	File:		SoundMemRecord.java
	
	Description:	This code shows using the SoundManager SPB calls for 
                        simple audio recording to memory.
        
	Author:		Apple Computer, Inc.

	Copyright: 	© Copyright 2002 Apple Computer, Inc. All rights reserved.
	
	Disclaimer:	IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
				("Apple") in consideration of your agreement to the following terms, and your
				use, installation, modification or redistribution of this Apple software
				constitutes acceptance of these terms.  If you do not agree with these terms,
				please do not use, install, modify or redistribute this Apple software.

				In consideration of your agreement to abide by the following terms, and subject
				to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
				copyrights in this original Apple software (the "Apple Software"), to use,
				reproduce, modify and redistribute the Apple Software, with or without
				modifications, in source and/or binary forms; provided that if you redistribute
				the Apple Software in its entirety and without modifications, you must retain
				this notice and the following text and disclaimers in all such redistributions of
				the Apple Software.  Neither the name, trademarks, service marks or logos of
				Apple Computer, Inc. may be used to endorse or promote products derived from the
				Apple Software without specific prior written permission from Apple.  Except as
				expressly stated in this notice, no other rights or licenses, express or implied,
				are granted by Apple herein, including but not limited to any patent rights that
				may be infringed by your derivative works or by other works in which the Apple
				Software may be incorporated.

				The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
				WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
				WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
				PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
				COMBINATION WITH YOUR PRODUCTS.

				IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
				CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
				GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
				ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
				OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
				(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
				ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
				
	Change History (most recent first):
            11/22/2002	md	New SampleCode revisions.
*/

import java.awt.*;
import java.awt.event.*;

import quicktime.*;
import quicktime.sound.*;
import quicktime.util.*;

public class SoundMemRecord extends Frame {	
	private static int
		kOSType =  1 << 1;
	private static int sndBufferSize = 327680;
	
	public static void main (String args[]) {
            System.out.println ("Make sure you have connected an Input device or have a built in audio input device.");
		SoundMemRecord sr = new SoundMemRecord ("Sound Recording");
		sr.show();
		sr.toFront();
	}
		
		// the sndHndl is kept around as it must be cached while we're using it
	SndHandle sndHndl;
	SPBDevice sndDevice;
	SPB recorder;
	
	SoundMemRecord (String title) {
		super (title);
		try {
			QTSession.open();
			sndDevice = new SPBDevice (null, SoundConstants.siWritePermission);
			System.out.println ("OptionsDialog==" + sndDevice.hasOptionsDialog());
			System.out.println ("NumChannels=" + sndDevice.getChannelAvailable());
			System.out.println ("SampleSize=" + sndDevice.getSampleSize());
			System.out.println ("SampleRate=" + sndDevice.getSampleRate());
			printArray ("Names", sndDevice.getInputSourceNames());
			printArray("Compression", sndDevice.getCompressionAvailable(), kOSType);
			printArray("SampleSize", sndDevice.getSampleSizeAvailable(), 0);
			printArray("SampleRate", sndDevice.getSampleRateAvailable());
			sndDevice.setPlayThruOnOff (0);
			sndHndl = new SndHandle (sndDevice.getNumberChannels(), 
											sndDevice.getSampleRate(), 
											sndDevice.getSampleSize(), 
											sndDevice.getCompressionType(), 
											60);
			sndHndl.appendSoundBuffer (sndBufferSize);	//this is the size of our sound buffer


// Set up recording to reuse object for multiple recordings
	// we hold onto this variable so that it isn't finalized before we
	// finish recording
	// we also need to remove the completion proc as we shut this down - see closingWindow
			System.out.println ("Will Record:" + sndDevice.bytesToMilliseconds(sndBufferSize) + " msecs");
			recorder = new SPB(sndDevice, 0, sndDevice.bytesToMilliseconds(sndBufferSize), sndHndl.getSoundData());
	
	// We're going to record this block ASynchronously
	// and we're installing a completion proc to notify us when
	// the recording is finished				
			recorder.setCompletionProc (new SICompletion () {
				final SndHandle soundHdl = sndHndl;
				final SPBDevice device = sndDevice;
						
				public void execute (SPB paramBlock) {
					System.out.println ("FinishedRecording");
					try{					
			// set up sndHndl after recording is finished so we cam play it
						soundHdl.setupHeader (device.getNumberChannels(), 
											device.getSampleRate(), 
											device.getSampleSize(), 
											device.getCompressionType(), 
											60,
											paramBlock.getCount());
					} catch (QTException ee){
						ee.printStackTrace();
					}	
				}
			});

		} catch (Exception ee) {
			ee.printStackTrace();
			QTSession.close();
		}
		

		setLayout(new GridLayout(1, 3, 2, 2));

		startButton.addActionListener (new ActionListener () {
			public void actionPerformed (ActionEvent event) {
				try{					
					System.out.println ("StartRecording");
					recorder.record (true);
				} catch (QTException ee){
					ee.printStackTrace();
				}	
			}
		});
		add (startButton);

		playRecordedButton.addActionListener (new ActionListener () {
			public void actionPerformed (ActionEvent event) {
				try{
					if (sndHndl != null) {
		// As with all other callbacks the application is reponsible for 
		// disposing of the callback - in this case that means disposing the sound channel.
		// So if using a callback the last thing you should do 
		// is install a callback command on the channel and dispose it when that guy fires
		// see SoundAction ....
						SndChannel sndChan1 = new SndChannel (new SoundCallBack () {
							SndHandle theSnd = sndHndl;
							
							public void execute (SndChannel sc) {
								try {
										System.out.println ("In Execute:" + sc);
									if (theSnd != null) {
										SndInfo info = theSnd.parseSndHeader ();
										SoundComponentData data = info.sndData;
										
											//shows use of buffer command
											// this call will LOCK the handle - it must stay locked whilst the buffer command is active
										QTPointerRef sndData = theSnd.getSoundData();
										ExtScheduledSoundHeader buffer = new ExtScheduledSoundHeader (sndData,//data.getBuffer(),
																						data.getNumChannels(),
																						data.getSampleRate(),
																						data.getSampleSize(),
																						data.getFormat());
										SndCommand bufferCmd = new SndCommand (SoundConstants.bufferCmd);
										bufferCmd.setBuffer (buffer);
										sc.doCommand (bufferCmd);
										
										theSnd = null;
											// reschedule callback to dispose the SndChannel
											// do it this way so that we don't have to wait
											// until the sound is finished playing
										sc.doCommand (new SndCommand (SoundConstants.callBackCmd));
										System.out.println ("Replay the sound just for fun:" + sc);
									} else {
										System.out.println ("Finished Playing:" + sc);
										sc.disposeQTObject();//finished with SndChannel so we MUST dispose it
									}
									
								} catch (Exception e) {
									e.printStackTrace();
								}
							}
						});
		// this plays asynchronously
		// we issue a callback on the SndChannel which will 
		// fire AFTER the snd play has completed
						sndChan1.play (sndHndl);
						sndChan1.doCommand (new SndCommand (SoundConstants.callBackCmd));
						System.out.println ("finished scheduling play and callback:" + sndChan1);
					}
				} catch (Exception ee){
					ee.printStackTrace();
				}	
			}
		});
		add (playRecordedButton);
		
		playMyDataButton.addActionListener (new ActionListener () {
			public void actionPerformed (ActionEvent event) {
				try{
					System.out.println ("Play constructed data");
					Thread.yield(); // make sure we get print feedback!!!
					
				// CONSTRUCT a Sound in memory and play it
						//use this constructor then do setup header with data size
					SndHandle tempSndHndl = new SndHandle ();

					byte[] media = new byte[64000];
					short val = -32768;

					for (int i = 0; i < media.length; i+=2) {
						if (val > 32750)
							val = -32768;
						val+=16;
						media[i] = (byte)((val | 0xFF00) >>> 8);
						media[i + 1] = (byte)val;	
					}

					// MUST do this first
					tempSndHndl.setupHeader (1, 11050, 16, SoundConstants.k16BitBigEndianFormat, 0, media.length);
						
						// either way works
					if (false) {
							// allocates the extra memory for the sound buffer in the SndHandle
						tempSndHndl.appendSoundBuffer (media.length);
						QTPointerRef qtpr=tempSndHndl.getSoundData();
						qtpr.copyFromArray (0,media,0,media.length);
					} else {
						QTPointerRef qtpr= new QTPointer (media.length, true);
						qtpr.copyFromArray (0,media,0,media.length);
							// sets the data directly
						tempSndHndl.setSoundData (qtpr);
					}
					
					Sound.play(tempSndHndl);

					System.out.println ("Finished Playing");
				} catch (Exception ee){
					ee.printStackTrace();
				}	
			}
		});
		add (playMyDataButton);

		pack();
		
		addWindowListener (new WindowAdapter () {
			public void windowClosing (WindowEvent e) {
				recorder.removeCompletionProc(); //clean this up as we installed it
				QTSession.close();
				dispose();
			}

			public void windowClosed (WindowEvent e) { 
				System.exit(0);
			}
		});
	}
	
	private static void printArray (String prefix, int[] array, int printTypeFlag) {
		System.out.print (prefix + "=[");
		if (array.length == 0) {
			System.out.println ("]");
			return;
		}
		for (int i = 0; i < array.length - 1; i++) {
			if ((printTypeFlag & kOSType) != 0)
				System.out.print (QTUtils.fromOSType(array[i]) + ",");
			else
				System.out.print (array[i] + ",");
		}
		if ((printTypeFlag & kOSType) != 0)
			System.out.println (QTUtils.fromOSType(array[array.length-1]) + "]");
		else
			System.out.println (array[array.length-1] + "]");
	}
	
	private static void printArray (String prefix, float[] array) {
		System.out.print (prefix + "=[");
		if (array.length == 0) {
			System.out.println ("]");
			return;
		}
		for (int i = 0; i < array.length - 1; i++) {
			System.out.print (array[i] + "F,");
		}
		System.out.println (array[array.length-1] + "F]");
	}

	private static void printArray (String prefix, String[] array) {
		System.out.print (prefix + "=[");
		if (array.length == 0) {
			System.out.println ("]");
			return;
		}
		for (int i = 0; i < array.length - 1; i++) {
			System.out.print (array[i] + ",");
		}
		System.out.println (array[array.length-1] + "]");
	}

	private Button startButton = new Button("Record");
	private Button playRecordedButton = new Button("Play Recorded Data");
	private Button playMyDataButton = new Button("Play My Data");
	private boolean recording = false;
}
