source: trunk/src/org/apollo/gui/DualPeakTroughWaveFormRenderer.java@ 1007

Last change on this file since 1007 was 1007, checked in by davidb, 8 years ago

Generalization of audio support to allow playback/mixer to be stereo, plus some edits to comments

File size: 6.0 KB
Line 
1package org.apollo.gui;
2
3import javax.sound.sampled.AudioFormat;
4
5import org.apollo.audio.SampledAudioManager;
6
7/**
8 * A WaveFormRenderer where the peaks and troughs are always chosen for every chunk of aggregated frames.
9 *
10 * @author Brook Novak
11 *
12 */
13public class DualPeakTroughWaveFormRenderer implements WaveFormRenderer {
14
15 private int sampleSize;
16 private int numChannels;
17 private boolean isBigEndian;
18 private boolean isSigned;
19
20 /**
21 * Constructor.
22 *
23 * @param audioFormat
24 * The format of the audio bytes to be rendered.
25 *
26 * @throws NullPointerException
27 * If audio format is null.
28 *
29 * @throws IllegalArgumentException
30 * If audioformat is not supported. See SampledAudioManager.isFormatSupportedForPlayback
31 */
32 public DualPeakTroughWaveFormRenderer(AudioFormat audioFormat) {
33 if (audioFormat == null) throw new NullPointerException("audioFormat");
34
35 if (!SampledAudioManager.getInstance().isFormatSupportedForPlayback(audioFormat))
36 throw new IllegalArgumentException();
37
38 sampleSize = audioFormat.getSampleSizeInBits();
39 numChannels = audioFormat.getChannels();
40 isSigned = audioFormat.getEncoding().toString().startsWith("PCM_SIGN");
41 isBigEndian = audioFormat.isBigEndian();
42
43 }
44
45 /**
46 * Renders waveforms in a given array of audio samples - producing an array of wave-form
47 * amplitutes ranging for -1.0 to 1.0.
48 *
49 * A single height is calculated for one or more frames, which is specified by the
50 * aggregationSize. The way in which waveforms are rendered is implementation dependant.
51 *
52 * @param audioBytes
53 * The array of pure samples.
54 *
55 * @param startFrame
56 * The starting frame to begin rendering
57 *
58 * @param frameLength
59 * The amount of frames to consider for rendering.
60 *
61 * @param aggregationSize
62 * The amout of frames to aggregate.
63 *
64 * @return
65 * An array of wave-form amplitutes ranging for -1.0 to 1.0.
66 * Note that this will be empty if aggregationSize > frameLength.
67 * If aggregationSize is one, then the returned array should be rendered
68 * as joint lines. Otherwise and implicit 2D array is returned: where
69 * the peak is an even index and a trough is a odd index (elements are
70 * interleaves in rendering order)
71 */
72 public float[] getSampleAmplitudes(byte[] audioBytes, int startFrame, int frameLength, int aggregationSize) {
73 assert(audioBytes != null);
74 assert(startFrame >= 0);
75 assert((startFrame + frameLength) <= (audioBytes.length / (sampleSize / 8)));
76
77 int aggregationCount = frameLength / aggregationSize;
78
79 float[] amplitudes = (aggregationSize == 1) ?
80 new float[aggregationCount] :
81 new float[aggregationCount * 2];
82
83 if (sampleSize == 16) {
84
85 int shift_multiplier = numChannels; // <<1=16-bit-mono, <<2=16-bit-stereo
86 for (int i = 0; i < aggregationCount; i++) {
87
88 int max = 0, min = 0, sample; // could use short, but int avoids casting everywhere
89
90 int startFrameIndex = (startFrame + (i * aggregationSize)) << shift_multiplier;
91 int endFrameIndex = startFrameIndex + (aggregationSize << shift_multiplier);
92
93 for (int k = startFrameIndex; k < endFrameIndex; k+=2) {
94
95 // k+=2 works for both mono and stereo
96 // in the case of stereo k+=2 alternates between L and R values.
97 // net effect is that it still finds the min and max values across
98 // all samples: startFrameIndex .. endFrameIndex
99
100 int lsb, msb;
101
102 if (isBigEndian) {
103
104 // First byte is MSB (high order)
105 msb = (int)audioBytes[k];
106
107 // Second byte is LSB (low order)
108 lsb = (int)audioBytes[k + 1];
109
110 } else {
111 // First byte is LSB (low order)
112 lsb = (int)audioBytes[k];
113
114 // Second byte is MSB (high order)
115 msb = (int)audioBytes[k + 1];
116 }
117
118 sample = (msb << 0x8) | (0xFF & lsb);
119
120 if (sample > max)
121 max = sample;
122 else if (sample < min)
123 min = sample;
124
125 }
126
127 if (aggregationSize == 1) {
128 amplitudes[i] = ((float)max) / 32768.0f;
129 } else {
130 amplitudes[(2 * i)] = ((float)max) / 32768.0f;
131 amplitudes[(2 * i) + 1] = ((float)min) / 32768.0f;
132 }
133
134 }
135
136 } else if (sampleSize == 8) {
137
138 int shift_multiplier = numChannels-1; // <<0=8-bit-mono, <<1=8-bit-stereo
139
140 // 'i' loop below works for either mono or stereo without any adjustment
141 // for same reason above given for 'k' loop
142
143 if (isSigned) {
144
145
146 // Find the peak within the block of aggregated frames
147 for (int i = 0; i < amplitudes.length; i++) {
148
149 byte max = 0, absmax = -1, sample, abssample;
150
151 int startFrameIndex = (startFrame + (i * aggregationSize)) << shift_multiplier;
152 int endFrameIndex = startFrameIndex + (aggregationSize << shift_multiplier);
153
154 for (int k = startFrameIndex; k < endFrameIndex; k++) {
155
156 sample = audioBytes[k];
157 abssample = (sample < 0) ? (byte)(sample * -1) : sample;
158
159 if (abssample > absmax) {
160 max = sample;
161 absmax = abssample;
162 }
163 }
164
165 amplitudes[i] = ((float)max) / 128.0f;
166
167 }
168
169 } else { // unsigned
170
171 // Find the peak within the block of aggregated frames
172 for (int i = 0; i < amplitudes.length; i++) {
173
174 int max = 0, absmax = -1, sample, abssample; // could use short, but int avoid casting everywhere
175
176 int startFrameIndex = (startFrame + (i * aggregationSize)) << shift_multiplier;
177 int endFrameIndex = startFrameIndex + (aggregationSize<<shift_multiplier);
178
179 for (int k = startFrameIndex; k < endFrameIndex; k++) {
180
181 sample = (audioBytes[k] & 0xFF) - 128;
182 abssample = Math.abs(sample);
183
184 if (abssample > absmax) {
185 max = sample;
186 absmax = abssample;
187 }
188 }
189
190 amplitudes[i] = ((float)max) / 128.0f;
191
192 }
193
194 }
195
196 }
197
198 return amplitudes;
199 }
200
201}
Note: See TracBrowser for help on using the repository browser.