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.InputStreamReader;
24  import java.nio.ByteBuffer;
25  import java.util.EnumSet;
26  
27  import net.sf.jack4j.AbstractJackClient;
28  import net.sf.jack4j.JackLocalPort;
29  import net.sf.jack4j.JackPortFlag;
30  import net.sf.jack4j.util.file.wav.WavFileWriter;
31  
32  /**
33   * Simple WAV recorder.
34   * 
35   * <p>
36   * See comments in the code that describe the usage of Jack4j library.
37   * 
38   * <p>
39   * To run the examples, you need to have the native Jack4j library in your
40   * system load path (LD_LIBRARY_PATH under Linux).
41   * 
42   * <p>
43   * This example client expects single command line parameter - client name.
44   * 
45   * @author repa
46   * 
47   */
48  public class WavRecorder extends AbstractJackClient {
49  
50  	private JackLocalPort portL;
51  	private JackLocalPort portR;
52  	private WavFileWriter wavFileWriter;
53  
54  	/**
55  	 * This is callback method, invoked by Jack client thread every now and
56  	 * then.
57  	 * 
58  	 * <p>
59  	 * This method actually works with the sound. As this client has input ports
60  	 * registered, each <i>buffer</i> attached to <i>input port</i> will
61  	 * contain <code>bufferSize</code> samples before this method is called.
62  	 * 
63  	 * <p>
64  	 * Ports were created earlier, during client initialization.
65  	 */
66  	@Override
67  	public int process(int bufferSize) throws Exception {
68  		// we have valid JackLocalPort instances in portL and portR variables; obtain their buffers.
69  		ByteBuffer[] buffers = new ByteBuffer[] { portL.getByteBuffer(bufferSize), portR.getByteBuffer(bufferSize) };
70  
71  		// ALWAYS clear these buffers before use! Otherwise, limit and position would be undefined.
72  		// Remember that calling clear() method doesn't really clear the data; it just resets position and limit.
73  		for (ByteBuffer buffer : buffers) {
74  			buffer.clear();
75  		}
76  
77  		// At this moment, we are ready to go: we can call getFloat method on each of the buffers
78  		// exactly bufferSize times.
79  		if (wavFileWriter != null) {
80  			// synchronize on wavFileWriter, so it won't be closed from the run() method during writing
81  			synchronized (wavFileWriter) {
82  				// Let WavFileReader do the real work; it's a bit complicated class,
83  				// but it ultimately calls getFloat method many times on each buffer.
84  				wavFileWriter.writeSamples(buffers, bufferSize);
85  			}
86  		}
87  
88  		// indicate that we finished properly
89  		return 0;
90  	}
91  
92  	/**
93  	 * Constructor.
94  	 * 
95  	 * <p>
96  	 * Registers under given client name, sets the callbacks and adds ports.
97  	 */
98  	public WavRecorder(String clientName) throws Exception {
99  		// Call to super constructor will register us with Jack server
100 		super(clientName, false, true, null);
101 
102 		// Setting default ThreadInit callback is necessary, 
103 		// otherwise other callbacks won't work properly (the Jack client thread
104 		// must be registered with JVM first)
105 		setDefaultThreadInitCallback();
106 
107 		// Set the native callback that will invoke our process(int) method.
108 		setDefaultProcessCallback();
109 
110 		// Add a pair of input audio ports
111 		portL = addAudioPort("left", EnumSet.of(JackPortFlag.IS_INPUT));
112 		portR = addAudioPort("right", EnumSet.of(JackPortFlag.IS_INPUT));
113 
114 		// we are not activated yet, thus the callbacks won't be invoked
115 	}
116 
117 	/**
118 	 * Activation and main event loop.
119 	 */
120 	public void run() throws Exception {
121 		// Activate the client; until now, callbacks weren't invoked
122 		activate();
123 
124 		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
125 
126 		System.out.println("CONNECT SOMETHING TO THE INPUT PORTS, otherwise you'll be recording silence");
127 		System.out.println("(you can use jack_connect or qjackctl to do this)");
128 
129 		while (true) {
130 			System.out.print("Enter name of the file to write to, or empty line to quit:");
131 			String wavFileName = in.readLine();
132 
133 			if ("".equals(wavFileName)) {
134 				break;
135 			}
136 
137 			System.out.print("Enter number of channels: ");
138 			String channelCountStr = in.readLine();
139 			int channelCount;
140 			try {
141 				channelCount = Integer.parseInt(channelCountStr);
142 			} catch (NumberFormatException e) {
143 				System.err.println("Not a number");
144 				continue;
145 			}
146 			if (channelCount < 1) {
147 				System.err.println("Wrong number of channels");
148 				continue;
149 			}
150 
151 			System.out.print("Enter number of bits per sample (1-32): ");
152 			String bitsPerSampleStr = in.readLine();
153 			int bitsPerSample;
154 			try {
155 				bitsPerSample = Integer.parseInt(bitsPerSampleStr);
156 			} catch (NumberFormatException e) {
157 				System.err.println("Not a number");
158 				continue;
159 			}
160 			if ((bitsPerSample < 1) || (bitsPerSample > 32)) {
161 				System.err.println("Wrong number of bits");
162 				continue;
163 			}
164 
165 			try {
166 				wavFileWriter = new WavFileWriter(wavFileName, bitsPerSample, getSampleRate(), channelCount);
167 			} catch (Exception e) {
168 				e.printStackTrace();
169 				continue;
170 			}
171 
172 			System.out.print("Recording, press enter to stop... ");
173 			in.readLine();
174 			try {
175 				// synchronize on the wavFileWriter so we won't close the writer during process() method
176 				synchronized (wavFileWriter) {
177 					wavFileWriter.close();
178 					wavFileWriter = null;
179 				}
180 			} catch (Exception e) {
181 				e.printStackTrace();
182 				continue;
183 			}
184 		}
185 
186 		close();
187 	}
188 
189 	public static void main(String[] args) throws Exception {
190 		if (args.length != 1) {
191 			System.err.println("Expecting parameter: clientName");
192 			return;
193 		}
194 		WavRecorder wavRecorder = new WavRecorder(args[0]);
195 
196 		wavRecorder.run();
197 	}
198 }