Source code: com/anotherbigidea/flash/sound/MP3Frame.java
1 /****************************************************************
2 * Copyright (c) 2001, David N. Main, All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the
6 * following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 *
17 * 3. The name of the author may not be used to endorse or
18 * promote products derived from this software without specific
19 * prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 ****************************************************************/
34 package com.anotherbigidea.flash.sound;
35
36 import java.io.*;
37 import java.util.*;
38 import com.anotherbigidea.io.*;
39 import com.anotherbigidea.flash.*;
40 import com.anotherbigidea.flash.structs.*;
41 import com.anotherbigidea.flash.interfaces.*;
42
43 /**
44 * An MP3 sound data frame.
45 */
46 public class MP3Frame
47 {
48 public static final int MPEG_Version_2_5 = 0;
49 public static final int MPEG_Version_2 = 2;
50 public static final int MPEG_Version_1 = 3;
51
52 public static final int MPEG_Layer_3 = 1;
53 public static final int MPEG_Layer_2 = 2;
54 public static final int MPEG_Layer_1 = 3;
55
56 public static final int CHANNEL_MODE_STEREO = 0;
57 public static final int CHANNEL_MODE_JOINT_STEREO = 1;
58 public static final int CHANNEL_MODE_DUAL_CHANNEL = 2;
59 public static final int CHANNEL_MODE_MONO = 3;
60
61 public static final int EMPHASIS_NONE = 0;
62 public static final int EMPHASIS_50_15_MS = 1;
63 public static final int EMPHASIS_RESERVED = 2;
64 public static final int EMPHASIS_CCIT_J17 = 3;
65
66 protected static final int[] MPEG1BitRates = { 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0 };
67 protected static final int[] MPEG2BitRates = { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 0 };
68
69 protected static final int[] MPEG10SampleRates = { 44100, 48000, 32000, 0 };
70 protected static final int[] MPEG20SampleRates = { 22050, 24000, 16000, 0 };
71 protected static final int[] MPEG25SampleRates = { 11025, 12000, 8000, 0 };
72
73 private static int FRAME_SAMPLES_MPEG_1 = 1152;
74 private static int FRAME_SAMPLES_MPEG_2 = 576;
75
76 protected int mpegVersion;
77 protected int mpegLayer;
78 protected boolean isProtected;
79 protected int bitRate;
80 protected int sampleRate;
81 protected boolean padded;
82 protected int channelMode;
83 protected int modeExtension;
84 protected boolean copyrighted;
85 protected boolean original;
86 protected int emphasis;
87 protected byte[] data;
88
89 protected int bit_rate;
90 protected int sample_rate;
91
92 public int getBitRate() { return bitRate; }
93 public int getSampleRate() { return sampleRate; }
94 public boolean isStereo() { return channelMode != this.CHANNEL_MODE_MONO; }
95 public int getDataLength() { return data.length; }
96
97 public int getSamplesPerFrame()
98 {
99 if( mpegVersion == this.MPEG_Version_1 ) return this.FRAME_SAMPLES_MPEG_1;
100 else return this.FRAME_SAMPLES_MPEG_2;
101 }
102
103 /**
104 * Read the next MP3 frame from the stream - return null if no more
105 */
106 public static MP3Frame readFrame( InputStream in ) throws IOException
107 {
108 MP3Frame frame = new MP3Frame();
109
110 while( true )
111 {
112 int b = in.read();
113
114 if( b < 0 ) return null;
115 if( b == 0xFF )
116 {
117 b = in.read();
118
119 if( b < 0 ) return null;
120 if( (b & 0xE0) != 0xE0 ) continue;
121
122 frame.mpegVersion = ( b & 0x18 ) >> 3;
123 frame.mpegLayer = ( b & 0x06 ) >> 1;
124 frame.isProtected = ( b & 1 ) == 0;
125
126 if( frame.mpegLayer != MPEG_Layer_3 ) continue;
127 break;
128 }
129 }
130
131 //skip the CRC
132 if( frame.isProtected )
133 {
134 in.read();
135 in.read();
136 }
137
138 int b = in.read();
139 if( b < 0 ) return null;
140
141 frame.bit_rate = ( b & 0xF0 ) >> 4;
142
143 if( frame.mpegVersion == MPEG_Version_1 ) frame.bitRate = MPEG1BitRates[frame.bit_rate];
144 else frame.bitRate = MPEG2BitRates[frame.bit_rate];
145
146 frame.sample_rate = ( b & 0x0C ) >> 2;
147 if ( frame.mpegVersion == MPEG_Version_1 ) frame.sampleRate = MPEG10SampleRates[frame.sample_rate];
148 else if( frame.mpegVersion == MPEG_Version_2 ) frame.sampleRate = MPEG20SampleRates[frame.sample_rate];
149 else frame.sampleRate = MPEG25SampleRates[frame.sample_rate];
150
151 frame.padded = ( b & 2 ) != 0;
152
153 b = in.read();
154 if( b < 0 ) return null;
155
156 frame.channelMode = ( b & 0xC0 ) >> 6;
157 frame.modeExtension = ( b & 0x30 ) >> 4;
158
159 frame.copyrighted = (( b & 0x80 ) >> 3) != 0;
160 frame.original = (( b & 0x40 ) >> 2) != 0;
161
162 frame.emphasis = b & 0x02;
163
164 int size = (((frame.mpegVersion == MPEG_Version_1 ? 144 : 72)
165 * frame.bitRate ) / frame.sampleRate ) +
166 (frame.padded ? 1 : 0) - 4;
167
168 byte[] data = new byte[size];
169 int read = 0;
170 int r;
171
172 while( (r = in.read( data, read, size - read )) >= 0 && read < size )
173 {
174 read += r;
175 }
176
177 if( read != size ) throw new IOException( "Unexpected end of MP3 data" );
178
179 frame.data = data;
180
181 //System.out.print( "." );
182 return frame;
183 }
184
185 public MP3Frame( ) {}
186
187 public void write( OutputStream out ) throws IOException
188 {
189 out.write( 0xff );
190
191 int b = 0xE1;
192 b |= this.mpegVersion << 3;
193 b |= this.mpegLayer << 1;
194 out.write( b );
195
196 b = bit_rate << 4;
197 b |= sample_rate << 2;
198 if( padded ) b |= 2;
199 out.write( b );
200
201 b = channelMode << 6;
202 b |= modeExtension << 4;
203 b |= emphasis;
204 out.write( b );
205
206 out.write( data );
207 }
208
209 public String toString()
210 {
211 String version = null;
212 if ( mpegVersion == this.MPEG_Version_1 ) version = "1";
213 else if( mpegVersion == this.MPEG_Version_2 ) version = "2";
214 else if( mpegVersion == this.MPEG_Version_2_5 ) version = "2.5";
215
216 return "MP3 Frame: " +
217 " version=" + version +
218 " bit-rate=" + bitRate +
219 " sample-rate=" + sampleRate +
220 " stereo=" + (channelMode != this.CHANNEL_MODE_MONO);
221 }
222 }