View Javadoc

1   package net.sf.jack4j.examples;
2   
3   /*
4   Copyright (C) 2008 Ondrej Par
5   
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10  
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU Lesser General Public License for more details.
15  
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  
20  */
21  
22  import java.io.BufferedReader;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.InputStreamReader;
26  import java.nio.ByteBuffer;
27  import java.util.EnumSet;
28  
29  import net.sf.jack4j.AbstractJackClient;
30  import net.sf.jack4j.JackLocalPort;
31  import net.sf.jack4j.JackPortFlag;
32  import net.sf.jack4j.util.file.wav.WavFileReader;
33  
34  /**
35   * Simple WAV player.
36   * 
37   * <p>
38   * See comments in the code that describe the usage of Jack4j library.
39   * 
40   * <p>
41   * To run the examples, you need to have the native Jack4j library in your
42   * system load path (LD_LIBRARY_PATH under Linux).
43   * 
44   * <p>
45   * This example client expects single command line parameter - client name.
46   * 
47   * @author repa
48   * 
49   */
50  public class WavPlayer extends AbstractJackClient {
51  
52  	private JackLocalPort portL;
53  	private JackLocalPort portR;
54  	private WavFileReader wavFileReader;
55  
56  	/**
57  	 * This is callback method, invoked by Jack client thread every now and
58  	 * then.
59  	 * 
60  	 * <p>
61  	 * This method actually produces the sound. As this client only outputs the
62  	 * sound, the method is expected to put <code>bufferSize</code> samples
63  	 * into <i>buffer</i> associated with each <i>port</i>.
64  	 * 
65  	 * <p>
66  	 * Ports were created earlier, during client initialization.
67  	 * 
68  	 * <p>
69  	 * Note that this method is not synchronized, but the native code that
70  	 * invokes this method always obtains a lock on this JackClient instance, so
71  	 * it actually behaves as a synchronzied method.
72  	 */
73  	@Override
74  	public int process(int bufferSize) throws Exception {
75  		// we have valid JackLocalPort instances in portL and portR variables; obtain their buffers.
76  		ByteBuffer[] buffers = new ByteBuffer[] { portL.getByteBuffer(bufferSize), portR.getByteBuffer(bufferSize) };
77  
78  		// ALWAYS clear these buffers before use! Otherwise, limit and position would be undefined:
79  		for (ByteBuffer buffer : buffers) {
80  			buffer.clear();
81  		}
82  
83  		if (wavFileReader != null) {
84  			// Let WavFileReader do the real work; it's a bit complicated class,
85  			// but it ultimately calls putFloat method many times on each buffer.
86  			int samplesRead = wavFileReader.readSamples(buffers, bufferSize);
87  
88  			if (samplesRead < bufferSize) {
89  				// the WavFileReader couldn't read enough samples, which means that
90  				// it reached end of file. Throw the reader away.
91  				wavFileReader = null;
92  			}
93  		}
94  
95  		// Fill the rest of buffers with silence (in case there's no WAV to play,
96  		// or in case when the end of file was reached
97  		for (ByteBuffer buffer : buffers) {
98  			// compute position in samples
99  			int samplePosition = buffer.position() / (Float.SIZE / 8);
100 
101 			for (int i = samplePosition; i < bufferSize; i++) {
102 				// put zero sample into output buffer
103 				buffer.putFloat(0.0f);
104 			}
105 		}
106 
107 		// return zero, which indicates that method finished correctly
108 		return 0;
109 	}
110 
111 	/**
112 	 * Constructor.
113 	 * 
114 	 * <p>
115 	 * Registers under given client name, sets the callbacks and adds ports.
116 	 */
117 	public WavPlayer(String clientName) throws Exception {
118 		// Call to super constructor will register us with Jack server
119 		super(clientName, false, true, null);
120 
121 		// Setting default ThreadInit callback is necessary, 
122 		// otherwise other callbacks won't work properly (the Jack client thread
123 		// must be registered with JVM first)
124 		setDefaultThreadInitCallback();
125 
126 		// Set the native callback that will invoke our process(int) method.
127 		setDefaultProcessCallback();
128 
129 		// Add a pair of output audio ports
130 		portL = addAudioPort("left", EnumSet.of(JackPortFlag.IS_OUTPUT));
131 		portR = addAudioPort("right", EnumSet.of(JackPortFlag.IS_OUTPUT));
132 
133 		// we are not activated yet, thus the callbacks won't be invoked
134 	}
135 
136 	/**
137 	 * Activation and main event loop.
138 	 */
139 	public void run() throws Exception {
140 		// Activate the client; until now, callbacks weren't invoked
141 		activate();
142 
143 		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
144 
145 		System.out.println("CONNECT OUTPUT PORTS to audio card, or you'll not hear any sound");
146 		System.out.println("(you can use jack_connect or qjackctl to do this)");
147 
148 		System.out.println("Enter WAV file name to play, or empty line to quit");
149 		while (true) {
150 			System.out.print("file name> ");
151 			String wavFileName = in.readLine();
152 
153 			if ("".equals(wavFileName)) {
154 				break;
155 			} else {
156 				File wavFile = new File(wavFileName);
157 				if (!wavFile.exists()) {
158 					System.err.println("File not found");
159 					continue;
160 				}
161 
162 				try {
163 					wavFileReader = new WavFileReader((new FileInputStream(wavFile)).getChannel());
164 				} catch (Exception e) {
165 					e.printStackTrace();
166 					continue;
167 				}
168 
169 				System.out.println("WAV file: " + wavFileName);
170 				System.out.println("Avg bytes per second: " + wavFileReader.getAvgBytesPerSecond());
171 				System.out.println("Bits per sample: " + wavFileReader.getBitsPerSample());
172 				System.out.println("Block align: " + wavFileReader.getBlockAlign());
173 				System.out.println("Channel count: " + wavFileReader.getChannelCount());
174 				System.out.println("Samples per second: " + wavFileReader.getSamplesPerSecond());
175 			}
176 		}
177 
178 		close();
179 	}
180 
181 	public static void main(String[] args) throws Exception {
182 		if (args.length != 1) {
183 			System.err.println("Expecting parameter: clientName");
184 			return;
185 		}
186 		WavPlayer wavPlayer = new WavPlayer(args[0]);
187 
188 		wavPlayer.run();
189 	}
190 }