source: trunk/src/org/apollo/meldex/WavSample.java

Last change on this file was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

File size: 12.0 KB
Line 
1package org.apollo.meldex;
2
3
4import java.io.File;
5import java.util.ArrayList;
6import javax.sound.sampled.AudioFileFormat;
7import javax.sound.sampled.AudioFormat;
8import javax.sound.sampled.AudioInputStream;
9import javax.sound.sampled.AudioSystem;
10
11import org.expeditee.gio.EcosystemManager;
12
13public class WavSample
14{
15 // Data representing the currently loaded file at a high level of abstraction
16 private File file = null;
17 private AudioFormat format = null;
18 AudioInputStream audioData = null;
19
20 // The actual raw audio data (without header). This shouldn't need to be used often.
21 byte[] rawData = null;
22
23 // The standardised unsigned 8-bit mono data. This should be used instead of the raw data.
24 private byte[] stdData = null;
25
26
27 public WavSample(byte[] rawData, AudioFormat format)
28 {
29 this.rawData = rawData;
30 this.format = format;
31 }
32
33 public WavSample()
34 {
35 }
36
37
38 public WavSample(AudioInputStream data)
39 {
40 audioData = data;
41
42 buildSampleData();
43 }
44
45
46 // ----------------------------------------------------------------------------------
47 // Method : buildSampleData
48 // Returns : true - if the sample data was successfully built
49 // false - if the sample data was not successfully built
50 // ----------------------------------------------------------------------------------
51 private boolean buildSampleData()
52 {
53 // Reset the sample to the beginning
54 if (reset() == false) {
55 return false;
56 }
57
58 // Get the format of the loaded file
59 format = audioData.getFormat();
60
61 try {
62 // Get the length of the loaded sample
63 int rawLength = audioData.available();
64
65 // Load the sample data into an array ready for processing
66 rawData = new byte[rawLength];
67 audioData.read(rawData, 0, rawLength);
68 }
69 catch (Exception ex) {
70 EcosystemManager.getGraphicsManager().showDialog("Exception", "Exception occurred creating sample.\n\n" + ex);
71 return false;
72 }
73
74 // Sample data built successfully
75 return true;
76 }
77
78
79 // ----------------------------------------------------------------------------------
80 // Method : reset
81 // Returns : true - if the sample was successfully reset
82 // false - if the sample was not successfully reset
83 // ----------------------------------------------------------------------------------
84 public boolean reset()
85 {
86 // For some bizarre reason we cannot reset a loaded file, so it is necessary to reload it
87 if (file != null) {
88 try {
89 audioData = AudioSystem.getAudioInputStream(file);
90 }
91 catch (Exception ex) {
92 EcosystemManager.getGraphicsManager().showDialog("Exception", "Exception occurred reloading sample.\n\n" + ex);
93 return false;
94 }
95 }
96
97 // Otherwise we just reset the stream to the start, ready for playback
98 else if (audioData != null) {
99 try {
100 audioData.reset();
101 }
102 catch (Exception ex) {
103 EcosystemManager.getGraphicsManager().showDialog("Exception", "Exception occurred resetting audio stream.\n\n" + ex);
104 return false;
105 }
106 }
107
108 // Reset performed successfully
109 return true;
110 }
111
112
113 // ----------------------------------------------------------------------------------
114 // Method : getFormat
115 // Returns : null - if the sample does not have a specified format
116 // else - the AudioFormat of the audio sample
117 // ----------------------------------------------------------------------------------
118 public AudioFormat getFormat()
119 {
120 if (format == null) {
121 if (audioData != null) {
122 format = audioData.getFormat();
123 }
124 }
125
126 return format;
127 }
128
129
130 // ----------------------------------------------------------------------------------
131 // Method : getBytesPerSecond
132 // Returns : -1 - if the value could not be successfully calculated
133 // else - the number of bytes of audio data per second
134 // ----------------------------------------------------------------------------------
135 public int getBytesPerSecond()
136 {
137 if (format == null) {
138 return -1;
139 }
140
141 int frameSize = (format.getChannels() * (format.getSampleSizeInBits() / 8));
142 return (int) (format.getSampleRate() * frameSize);
143 }
144
145
146 // ----------------------------------------------------------------------------------
147 // Method : getStandardisedData
148 // Returns : null - if the sample could not be successfully standardised
149 // else - byte[] containing the standardised unsigned 8-bit mono data
150 // ----------------------------------------------------------------------------------
151 public byte[] getStandardisedData(boolean createIfNotComputed)
152 {
153 if (stdData == null && createIfNotComputed) {
154 // Create the standardised unsigned 8-bit mono data
155 // stdData = toStandardForm(format, rawData, rawData.length, (int) format.getSampleRate());
156 stdData = toStandardForm(format, rawData, (int) format.getSampleRate());
157 }
158
159 return stdData;
160 }
161
162
163 // ----------------------------------------------------------------------------------
164 // Method : toStandardForm
165 // Returns : null - if the sample could not be successfully standardised
166 // else - byte[] containing the standardised unsigned 8-bit mono data
167 // ----------------------------------------------------------------------------------
168 // public static byte[] toStandardForm(AudioFormat format, byte[] inData, int amount, int frequency)
169 public static byte[] toStandardForm(AudioFormat format, byte[] inData, int frequency)
170 {
171 // Check for (supposedly) invalid sample formats
172 if (format.getSampleSizeInBits() == 8) {
173 if (format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) {
174 EcosystemManager.getGraphicsManager().showDialog("Exception", "Oops! Internal Error: Unexpected audio format (signed 8-bit).");
175 return null;
176 }
177 }
178 if (format.getSampleSizeInBits() == 16) {
179 if (format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
180 EcosystemManager.getGraphicsManager().showDialog("Exception", "Oops! Internal Error: Unexpected audio format (unsigned 16-bit).");
181 return null;
182 }
183 }
184
185 int baseSampleSize = (int) (format.getSampleRate() / frequency);
186 int frameSize = (format.getChannels() * (format.getSampleSizeInBits() / 8));
187 int sampleSize = (baseSampleSize * frameSize);
188
189 // Create a new list to store the standardised sample values
190 ArrayList<Integer> stdTemp = new ArrayList<Integer>();
191
192 // Simply pick values from the sample at the frequency specified
193 // for (int i = 0; i < amount; i += sampleSize) {
194 for (int i = 0; i < inData.length; i += sampleSize) {
195 for (int j = 0; j < frameSize; j++) {
196 int val = inData[i+j];
197 stdTemp.add(new Integer(val));
198 }
199 }
200
201 // Convert the sample to standard unsigned, 8-bit, mono values
202 stdTemp = toStandardEndian(format, stdTemp);
203 stdTemp = toUnsignedValues(format, stdTemp);
204 stdTemp = to8bit(format, stdTemp);
205 stdTemp = toMono(format, stdTemp);
206
207 // Allocate memory for the standardised data
208 byte[] tmpData = new byte[stdTemp.size()];
209
210 // Convert the ArrayList back to an array of bytes
211 for (int i = 0; i < stdTemp.size(); i++) {
212 int val = ((Integer) stdTemp.get(i)).intValue();
213 tmpData[i] = (byte) val;
214 }
215
216 return tmpData;
217 }
218
219
220 private static ArrayList<Integer> toStandardEndian(AudioFormat format, ArrayList<Integer> inData)
221 {
222 // Endian details do not apply to 8-bit samples, so no conversion needed
223 if (format.getSampleSizeInBits() == 8) {
224 return inData;
225 }
226
227 // Create a new list to store the results
228 ArrayList<Integer> outData = new ArrayList<Integer>();
229
230 // Simply convert each number in the sample to the endian form for the current system
231 for (int i = 0; i < inData.size(); i += 2) {
232
233 int MSB, LSB;
234 if (format.isBigEndian()) {
235 MSB = ((Integer) inData.get(i)).intValue(); // First byte is MSB (high order)
236 LSB = ((Integer) inData.get(i+1)).intValue(); // Second byte is LSB (low order)
237 }
238 else {
239 LSB = ((Integer) inData.get(i)).intValue(); // First byte is LSB (low order)
240 MSB = ((Integer) inData.get(i+1)).intValue(); // Second byte is MSB (high order)
241 }
242
243 int val = (MSB << 8) + (LSB & 255);
244 outData.add(new Integer(val));
245 }
246
247 return outData;
248 }
249
250
251 private static ArrayList<Integer> toUnsignedValues(AudioFormat format, ArrayList<Integer> inData)
252 {
253 // PCM_UNSIGNED samples are what we want, so no conversion needed
254 if (format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
255 return inData;
256 }
257
258 // Create a new list to store the results
259 ArrayList<Integer> outData = new ArrayList<Integer>();
260
261 // Simply convert each signed number in the sample to an unsigned value
262 for (int i = 0; i < inData.size(); i++) {
263
264 int val = ((Integer) inData.get(i)).intValue();
265 val = val - 32768;
266 if (val < -32768) {
267 val += 65536;
268 }
269
270 outData.add(new Integer(val));
271 }
272
273 return outData;
274 }
275
276
277 private static ArrayList<Integer> to8bit(AudioFormat format, ArrayList<Integer> inData)
278 {
279 // 8-bit data is what we want, so no conversion necessary
280 if (format.getSampleSizeInBits() == 8) {
281 return inData;
282 }
283
284 // Create a new list to store the results
285 ArrayList<Integer> outData = new ArrayList<Integer>();
286
287 // Simply reduce each value in the sample to an 8-bit value
288 for (int i = 0; i < inData.size(); i++) {
289
290 int val = ((Integer) inData.get(i)).intValue();
291 val = val / 256;
292
293 outData.add(new Integer(val));
294 }
295
296 return outData;
297 }
298
299
300 private static ArrayList<Integer> toMono(AudioFormat format, ArrayList<Integer> inData)
301 {
302 // Mono-channel data is what we want, so no conversion necessary
303 if (format.getChannels() == 1) {
304 return inData;
305 }
306
307 // Create a new list to store the results
308 ArrayList<Integer> outData = new ArrayList<Integer>();
309
310 // Simply average all of the channels for each frame
311 for (int i = 0; i < inData.size(); i += format.getChannels()) {
312
313 int sum = 0;
314 for (int j = 0; j < format.getChannels(); j++) {
315 int val = ((Integer) inData.get(i+j)).intValue();
316 // We need to use the true unsigned value
317 if (val < 0) {
318 val = val + 256;
319 }
320 sum += val;
321 }
322
323 int val = sum / format.getChannels();
324 // Revert back to signed representation
325 if (val > 127) {
326 val = val - 256;
327 }
328
329 outData.add(new Integer(val));
330 }
331
332 return outData;
333 }
334
335
336 // ----------------------------------------------------------------------------------
337 // Method : loadFromFile
338 // Returns : false - if the track could not be successfully loaded
339 // true - if the track was successfully loaded
340 // ----------------------------------------------------------------------------------
341 public boolean loadFromFile(File wavFile)
342 {
343 file = wavFile;
344
345 return buildSampleData();
346 }
347
348
349 // ----------------------------------------------------------------------------------
350 // Method : saveToFile
351 // Returns : false - if the sample could not be successfully saved
352 // true - if the sample was successfully saved
353 // ----------------------------------------------------------------------------------
354 public boolean saveToFile(File outFile)
355 {
356 // Reset the sample to the beginning
357 if (reset() == false) {
358 return false;
359 }
360
361 // Write the audio data to the selected file in the format specified
362 try {
363 if (AudioSystem.write(audioData, AudioFileFormat.Type.WAVE, outFile) == -1) {
364 EcosystemManager.getGraphicsManager().showDialog("Exception", "Problem occurred writing to file.");
365 return false;
366 }
367 }
368 catch (Exception ex) {
369 EcosystemManager.getGraphicsManager().showDialog("Exception", "Exception occurred saving file.\n\n" + ex);
370 return false;
371 }
372
373 // File saved successfully
374 return true;
375 }
376
377 public byte[] getRawAudio() {
378 return rawData;
379 }
380}
Note: See TracBrowser for help on using the repository browser.