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

Last change on this file since 1102 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: 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 {
34 if (audioFormat == null) throw new NullPointerException("audioFormat");
35
36 if (!SampledAudioManager.getInstance().isFormatSupportedForPlayback(audioFormat)) 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 {
74 assert(audioBytes != null);
75 assert(startFrame >= 0);
76 assert((startFrame + frameLength) <= (audioBytes.length / (sampleSize / 8)));
77
78 int aggregationCount = frameLength / aggregationSize;
79
80 float[] amplitudes = (aggregationSize == 1) ? new float[aggregationCount] : new float[aggregationCount * 2];
81
82 if (sampleSize == 16) {
83
84 int shift_multiplier = numChannels; // <<1=16-bit-mono, <<2=16-bit-stereo
85 for (int i = 0; i < aggregationCount; i++) {
86
87 int max = 0, min = 0, sample; // could use short, but int avoids casting everywhere
88
89 int startFrameIndex = (startFrame + (i * aggregationSize)) << shift_multiplier;
90 int endFrameIndex = startFrameIndex + (aggregationSize << shift_multiplier);
91
92 for (int k = startFrameIndex; k < endFrameIndex; k+=2) {
93
94 // k+=2 works for both mono and stereo
95 // in the case of stereo k+=2 alternates between L and R values.
96 // net effect is that it still finds the min and max values across
97 // all samples: startFrameIndex .. endFrameIndex
98
99 int lsb, msb;
100
101 if (isBigEndian) {
102
103 // First byte is MSB (high order)
104 msb = (int)audioBytes[k];
105
106 // Second byte is LSB (low order)
107 lsb = (int)audioBytes[k + 1];
108
109 } else {
110 // First byte is LSB (low order)
111 lsb = (int)audioBytes[k];
112
113 // Second byte is MSB (high order)
114 msb = (int)audioBytes[k + 1];
115 }
116
117 sample = (msb << 0x8) | (0xFF & lsb);
118
119 if (sample > max)
120 max = sample;
121 else if (sample < min)
122 min = sample;
123
124 }
125
126 if (aggregationSize == 1) {
127 amplitudes[i] = ((float)max) / 32768.0f;
128 } else {
129 amplitudes[(2 * i)] = ((float)max) / 32768.0f;
130 amplitudes[(2 * i) + 1] = ((float)min) / 32768.0f;
131 }
132
133 }
134
135 } else if (sampleSize == 8) {
136
137 int shift_multiplier = numChannels-1; // <<0=8-bit-mono, <<1=8-bit-stereo
138
139 // 'i' loop below works for either mono or stereo without any adjustment
140 // for same reason above given for 'k' loop
141
142 if (isSigned) {
143
144
145 // Find the peak within the block of aggregated frames
146 for (int i = 0; i < amplitudes.length; i++) {
147
148 byte max = 0, absmax = -1, sample, abssample;
149
150 int startFrameIndex = (startFrame + (i * aggregationSize)) << shift_multiplier;
151 int endFrameIndex = startFrameIndex + (aggregationSize << shift_multiplier);
152
153 for (int k = startFrameIndex; k < endFrameIndex; k++) {
154
155 sample = audioBytes[k];
156 abssample = (sample < 0) ? (byte)(sample * -1) : sample;
157
158 if (abssample > absmax) {
159 max = sample;
160 absmax = abssample;
161 }
162 }
163
164 amplitudes[i] = ((float)max) / 128.0f;
165
166 }
167
168 } else { // unsigned
169
170 // Find the peak within the block of aggregated frames
171 for (int i = 0; i < amplitudes.length; i++) {
172
173 int max = 0, absmax = -1, sample, abssample; // could use short, but int avoid casting everywhere
174
175 int startFrameIndex = (startFrame + (i * aggregationSize)) << shift_multiplier;
176 int endFrameIndex = startFrameIndex + (aggregationSize<<shift_multiplier);
177
178 for (int k = startFrameIndex; k < endFrameIndex; k++) {
179
180 sample = (audioBytes[k] & 0xFF) - 128;
181 abssample = Math.abs(sample);
182
183 if (abssample > absmax) {
184 max = sample;
185 absmax = abssample;
186 }
187 }
188
189 amplitudes[i] = ((float)max) / 128.0f;
190
191 }
192
193 }
194
195 }
196
197 return amplitudes;
198 }
199
200}
Note: See TracBrowser for help on using the repository browser.