/tritonus-0.3.7/src/classes/org/tritonus/sampled/convert/lame/Mp3LameFormatConversionProvider.java

# · Java · 388 lines · 284 code · 30 blank · 74 comment · 55 complexity · 8df72ed39cce8cb0ff8e53d0cf85271b MD5 · raw file

  1. /*
  2. * Mp3LameFormatConversionProvider.java
  3. *
  4. * This file is part of Tritonus: http://www.tritonus.org/
  5. */
  6. /*
  7. * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU Library General Public License as published
  11. * by the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Library General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Library General Public
  20. * License along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22. */
  23. /*
  24. |<--- this code is formatted to fit into 80 columns --->|
  25. */
  26. package org.tritonus.sampled.convert.lame;
  27. import java.io.InputStream;
  28. import java.io.IOException;
  29. import java.util.Arrays;
  30. import java.util.Iterator;
  31. import javax.sound.sampled.AudioSystem;
  32. import javax.sound.sampled.AudioFormat;
  33. import javax.sound.sampled.AudioInputStream;
  34. import org.tritonus.share.TDebug;
  35. import org.tritonus.share.sampled.AudioFormatSet;
  36. import org.tritonus.share.sampled.convert.TSimpleFormatConversionProvider;
  37. import org.tritonus.share.sampled.convert.TAsynchronousFilteredAudioInputStream;
  38. import org.tritonus.lowlevel.lame.Lame;
  39. /**
  40. * ConversionProvider for encoding MP3 audio files with the lame lib.
  41. * <p>
  42. * It uses a sloppy implementation of the MPEG1L3 encoding: It is used as a
  43. * common denominator. So users can always ask for MPEG1L3 encoding but may get
  44. * in fact an MPEG2L3 or MPEG2.5L3 encoded stream.
  45. *
  46. * @author Florian Bomers
  47. */
  48. // TODO: add decoding ? more work on LAME itself...
  49. // TODO: byte swapping support in LAME ?
  50. public class Mp3LameFormatConversionProvider extends
  51. TSimpleFormatConversionProvider {
  52. private static final int ALL = AudioSystem.NOT_SPECIFIED;
  53. private static final int MPEG_BITS_PER_SAMPLE = ALL;
  54. private static final int MPEG_FRAME_RATE = ALL;
  55. @SuppressWarnings("unused")
  56. private static final int MPEG_FRAME_SIZE = ALL;
  57. public static final AudioFormat.Encoding MPEG1L3 = Lame.MPEG1L3;
  58. // Lame converts automagically to MPEG2 or MPEG2.5, if necessary.
  59. public static final AudioFormat.Encoding MPEG2L3 = Lame.MPEG2L3;
  60. public static final AudioFormat.Encoding MPEG2DOT5L3 = Lame.MPEG2DOT5L3;
  61. /**
  62. * Lame provides these formats:<br>
  63. * MPEG1 layer III samplerates(kHz): 32 44.1 48<br>
  64. * bitrates(kbs): 32 40 48 56 64 80 96 112 128 160 192 224 256 320<br>
  65. * <br>
  66. * MPEG2 layer III samplerates(kHz): 16 22.05 24<br>
  67. * bitrates(kbs): 8 16 24 32 40 48 56 64 80 96 112 128 144 160<br>
  68. * <br>
  69. * MPEG2.5 layer III samplerates(kHz): 8 11.025 12<br>
  70. * bitrates(kbs): 8 16 24 32 40 48 56 64 80 96 112 128 144 160<br>
  71. */
  72. private static final AudioFormat[] OUTPUT_FORMATS = {
  73. new AudioFormat(MPEG2DOT5L3, 8000, ALL, 1, ALL, ALL, false),
  74. new AudioFormat(MPEG2DOT5L3, 8000, ALL, 2, ALL, ALL, false),
  75. new AudioFormat(MPEG2DOT5L3, 8000, ALL, 1, ALL, ALL, true),
  76. new AudioFormat(MPEG2DOT5L3, 8000, ALL, 2, ALL, ALL, true),
  77. new AudioFormat(MPEG2DOT5L3, 11025, ALL, 1, ALL, ALL, false),
  78. new AudioFormat(MPEG2DOT5L3, 11025, ALL, 2, ALL, ALL, false),
  79. new AudioFormat(MPEG2DOT5L3, 11025, ALL, 1, ALL, ALL, true),
  80. new AudioFormat(MPEG2DOT5L3, 11025, ALL, 2, ALL, ALL, true),
  81. new AudioFormat(MPEG2DOT5L3, 12000, ALL, 1, ALL, ALL, false),
  82. new AudioFormat(MPEG2DOT5L3, 12000, ALL, 2, ALL, ALL, false),
  83. new AudioFormat(MPEG2DOT5L3, 12000, ALL, 1, ALL, ALL, true),
  84. new AudioFormat(MPEG2DOT5L3, 12000, ALL, 2, ALL, ALL, true),
  85. new AudioFormat(MPEG2L3, 16000, ALL, 1, ALL, ALL, false),
  86. new AudioFormat(MPEG2L3, 16000, ALL, 2, ALL, ALL, false),
  87. new AudioFormat(MPEG2L3, 16000, ALL, 1, ALL, ALL, true),
  88. new AudioFormat(MPEG2L3, 16000, ALL, 2, ALL, ALL, true),
  89. new AudioFormat(MPEG2L3, 22050, ALL, 1, ALL, ALL, false),
  90. new AudioFormat(MPEG2L3, 22050, ALL, 2, ALL, ALL, false),
  91. new AudioFormat(MPEG2L3, 22050, ALL, 1, ALL, ALL, true),
  92. new AudioFormat(MPEG2L3, 22050, ALL, 2, ALL, ALL, true),
  93. new AudioFormat(MPEG2L3, 24000, ALL, 1, ALL, ALL, false),
  94. new AudioFormat(MPEG2L3, 24000, ALL, 2, ALL, ALL, false),
  95. new AudioFormat(MPEG2L3, 24000, ALL, 1, ALL, ALL, true),
  96. new AudioFormat(MPEG2L3, 24000, ALL, 2, ALL, ALL, true),
  97. // automagic
  98. new AudioFormat(MPEG1L3, 8000, ALL, 1, ALL, ALL, false), // MPEG2DOT5L3
  99. new AudioFormat(MPEG1L3, 8000, ALL, 2, ALL, ALL, false), // MPEG2DOT5L3
  100. new AudioFormat(MPEG1L3, 8000, ALL, 1, ALL, ALL, true), // MPEG2DOT5L3
  101. new AudioFormat(MPEG1L3, 8000, ALL, 2, ALL, ALL, true), // MPEG2DOT5L3
  102. new AudioFormat(MPEG1L3, 11025, ALL, 1, ALL, ALL, false),// MPEG2DOT5L3
  103. new AudioFormat(MPEG1L3, 11025, ALL, 2, ALL, ALL, false),// MPEG2DOT5L3
  104. new AudioFormat(MPEG1L3, 11025, ALL, 1, ALL, ALL, true), // MPEG2DOT5L3
  105. new AudioFormat(MPEG1L3, 11025, ALL, 2, ALL, ALL, true), // MPEG2DOT5L3
  106. new AudioFormat(MPEG1L3, 12000, ALL, 1, ALL, ALL, false),// MPEG2DOT5L3
  107. new AudioFormat(MPEG1L3, 12000, ALL, 2, ALL, ALL, false),// MPEG2DOT5L3
  108. new AudioFormat(MPEG1L3, 12000, ALL, 1, ALL, ALL, true), // MPEG2DOT5L3
  109. new AudioFormat(MPEG1L3, 12000, ALL, 2, ALL, ALL, true), // MPEG2DOT5L3
  110. // automagic
  111. new AudioFormat(MPEG1L3, 16000, ALL, 1, ALL, ALL, false), // MPEG2L3
  112. new AudioFormat(MPEG1L3, 16000, ALL, 2, ALL, ALL, false), // MPEG2L3
  113. new AudioFormat(MPEG1L3, 16000, ALL, 1, ALL, ALL, true), // MPEG2L3
  114. new AudioFormat(MPEG1L3, 16000, ALL, 2, ALL, ALL, true), // MPEG2L3
  115. new AudioFormat(MPEG1L3, 22050, ALL, 1, ALL, ALL, false), // MPEG2L3
  116. new AudioFormat(MPEG1L3, 22050, ALL, 2, ALL, ALL, false), // MPEG2L3
  117. new AudioFormat(MPEG1L3, 22050, ALL, 1, ALL, ALL, true), // MPEG2L3
  118. new AudioFormat(MPEG1L3, 22050, ALL, 2, ALL, ALL, true), // MPEG2L3
  119. new AudioFormat(MPEG1L3, 24000, ALL, 1, ALL, ALL, false), // MPEG2L3
  120. new AudioFormat(MPEG1L3, 24000, ALL, 2, ALL, ALL, false), // MPEG2L3
  121. new AudioFormat(MPEG1L3, 24000, ALL, 1, ALL, ALL, true), // MPEG2L3
  122. new AudioFormat(MPEG1L3, 24000, ALL, 2, ALL, ALL, true), // MPEG2L3
  123. new AudioFormat(MPEG1L3, 32000, ALL, 1, ALL, ALL, false),
  124. new AudioFormat(MPEG1L3, 32000, ALL, 2, ALL, ALL, false),
  125. new AudioFormat(MPEG1L3, 32000, ALL, 1, ALL, ALL, true),
  126. new AudioFormat(MPEG1L3, 32000, ALL, 2, ALL, ALL, true),
  127. new AudioFormat(MPEG1L3, 44100, ALL, 1, ALL, ALL, false),
  128. new AudioFormat(MPEG1L3, 44100, ALL, 2, ALL, ALL, false),
  129. new AudioFormat(MPEG1L3, 44100, ALL, 1, ALL, ALL, true),
  130. new AudioFormat(MPEG1L3, 44100, ALL, 2, ALL, ALL, true),
  131. new AudioFormat(MPEG1L3, 48000, ALL, 1, ALL, ALL, false),
  132. new AudioFormat(MPEG1L3, 48000, ALL, 2, ALL, ALL, false),
  133. new AudioFormat(MPEG1L3, 48000, ALL, 1, ALL, ALL, true),
  134. new AudioFormat(MPEG1L3, 48000, ALL, 2, ALL, ALL, true),
  135. };
  136. private static final AudioFormat[] INPUT_FORMATS = {
  137. new AudioFormat(8000, 16, 1, true, false),
  138. new AudioFormat(8000, 16, 1, true, true),
  139. new AudioFormat(11025, 16, 1, true, false),
  140. new AudioFormat(11025, 16, 1, true, true),
  141. new AudioFormat(12000, 16, 1, true, false),
  142. new AudioFormat(12000, 16, 1, true, true),
  143. new AudioFormat(16000, 16, 1, true, false),
  144. new AudioFormat(16000, 16, 1, true, true),
  145. new AudioFormat(22050, 16, 1, true, false),
  146. new AudioFormat(22050, 16, 1, true, true),
  147. new AudioFormat(24000, 16, 1, true, false),
  148. new AudioFormat(24000, 16, 1, true, true),
  149. new AudioFormat(32000, 16, 1, true, false),
  150. new AudioFormat(32000, 16, 1, true, true),
  151. new AudioFormat(44100, 16, 1, true, false),
  152. new AudioFormat(44100, 16, 1, true, true),
  153. new AudioFormat(48000, 16, 1, true, false),
  154. new AudioFormat(48000, 16, 1, true, true),
  155. new AudioFormat(8000, 16, 2, true, false),
  156. new AudioFormat(8000, 16, 2, true, true),
  157. new AudioFormat(11025, 16, 2, true, false),
  158. new AudioFormat(11025, 16, 2, true, true),
  159. new AudioFormat(12000, 16, 2, true, false),
  160. new AudioFormat(12000, 16, 2, true, true),
  161. new AudioFormat(16000, 16, 2, true, false),
  162. new AudioFormat(16000, 16, 2, true, true),
  163. new AudioFormat(22050, 16, 2, true, false),
  164. new AudioFormat(22050, 16, 2, true, true),
  165. new AudioFormat(24000, 16, 2, true, false),
  166. new AudioFormat(24000, 16, 2, true, true),
  167. new AudioFormat(32000, 16, 2, true, false),
  168. new AudioFormat(32000, 16, 2, true, true),
  169. new AudioFormat(44100, 16, 2, true, false),
  170. new AudioFormat(44100, 16, 2, true, true),
  171. new AudioFormat(48000, 16, 2, true, false),
  172. new AudioFormat(48000, 16, 2, true, true),
  173. };
  174. /**
  175. * Constructor.
  176. */
  177. public Mp3LameFormatConversionProvider() {
  178. super(Arrays.asList(INPUT_FORMATS), Arrays.asList(OUTPUT_FORMATS));
  179. if (!Lame.isLibAvailable()) {
  180. disable();
  181. if (TDebug.TraceAudioConverter) {
  182. TDebug.out("******* Error initializing LAME mp3 encoder: "
  183. + Lame.getLinkError());
  184. }
  185. }
  186. }
  187. public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
  188. AudioInputStream audioInputStream) {
  189. if (isConversionSupported(targetFormat, audioInputStream.getFormat())) {
  190. return new EncodedMpegAudioInputStream(getDefaultTargetFormat(
  191. targetFormat, audioInputStream.getFormat(), false),
  192. audioInputStream);
  193. } else {
  194. throw new IllegalArgumentException("conversion not supported");
  195. }
  196. }
  197. public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding,
  198. AudioFormat sourceFormat) {
  199. if (TDebug.TraceAudioConverter) {
  200. TDebug.out(">MP3Lame getTargetFormats(AudioFormat.Encoding, AudioFormat):");
  201. TDebug.out("checking out possible target formats");
  202. TDebug.out("from: " + sourceFormat);
  203. TDebug.out("to : " + targetEncoding);
  204. }
  205. if (isConversionSupported(targetEncoding, sourceFormat)) {
  206. AudioFormatSet result = new AudioFormatSet();
  207. Iterator iterator = getCollectionTargetFormats().iterator();
  208. while (iterator.hasNext()) {
  209. AudioFormat targetFormat = (AudioFormat) iterator.next();
  210. // if (TDebug.TraceAudioConverter) {
  211. // TDebug.out("-checking target format "+targetFormat);
  212. // }
  213. if (doMatch(targetFormat.getSampleRate(),
  214. sourceFormat.getSampleRate())
  215. && targetFormat.getEncoding().equals(targetEncoding)
  216. && doMatch(targetFormat.getChannels(),
  217. sourceFormat.getChannels())) {
  218. targetFormat = getDefaultTargetFormat(targetFormat,
  219. sourceFormat, true);
  220. // if (TDebug.TraceAudioConverter) {
  221. // TDebug.out("-yes. added "+targetFormat);
  222. // }
  223. result.add(targetFormat);
  224. } // else {
  225. // if (TDebug.TraceAudioConverter) {
  226. // boolean e=targetFormat.getEncoding().equals(targetEncoding);
  227. // TDebug.out("-no.
  228. // \""+targetFormat.getEncoding()+"\"==\""+targetEncoding+"\" ?
  229. // "+e);
  230. // }
  231. // }
  232. }
  233. if (TDebug.TraceAudioConverter) {
  234. TDebug.out("<found " + result.size() + " matching formats.");
  235. }
  236. return result.toAudioFormatArray();
  237. } else {
  238. if (TDebug.TraceAudioConverter) {
  239. TDebug.out("<returning empty array.");
  240. }
  241. return EMPTY_FORMAT_ARRAY;
  242. }
  243. }
  244. protected AudioFormat getDefaultTargetFormat(AudioFormat targetFormat,
  245. AudioFormat sourceFormat, boolean allowNotSpecified) {
  246. // always set bits per sample to MPEG_BITS_PER_SAMPLE
  247. // set framerate to MPEG_FRAME_RATE, framesize to FRAME_SIZE
  248. // always retain sample rate
  249. float targetSampleRate = targetFormat.getSampleRate();
  250. if (targetSampleRate == AudioSystem.NOT_SPECIFIED) {
  251. targetSampleRate = sourceFormat.getSampleRate();
  252. }
  253. if ((!allowNotSpecified && targetSampleRate == AudioSystem.NOT_SPECIFIED)
  254. || (targetSampleRate != AudioSystem.NOT_SPECIFIED
  255. && sourceFormat.getSampleRate() != AudioSystem.NOT_SPECIFIED && targetSampleRate != sourceFormat.getSampleRate())) {
  256. throw new IllegalArgumentException("Illegal sample rate ("
  257. + targetSampleRate + ") !");
  258. }
  259. int targetChannels = targetFormat.getChannels();
  260. if (targetChannels == AudioSystem.NOT_SPECIFIED) {
  261. targetChannels = sourceFormat.getChannels();
  262. }
  263. if ((!allowNotSpecified && targetChannels == AudioSystem.NOT_SPECIFIED)
  264. || (targetChannels != AudioSystem.NOT_SPECIFIED
  265. && sourceFormat.getChannels() != AudioSystem.NOT_SPECIFIED && targetChannels != sourceFormat.getChannels())) {
  266. throw new IllegalArgumentException("Illegal number of channels ("
  267. + targetChannels + ") !");
  268. }
  269. AudioFormat newTargetFormat = new AudioFormat(
  270. targetFormat.getEncoding(), targetSampleRate,
  271. MPEG_BITS_PER_SAMPLE, targetChannels, getFrameSize(
  272. targetFormat.getEncoding(), targetSampleRate,
  273. MPEG_BITS_PER_SAMPLE, targetChannels, MPEG_FRAME_RATE,
  274. false, 0), MPEG_FRAME_RATE, false, targetFormat.properties());
  275. return newTargetFormat;
  276. }
  277. // implementation from TSimpleFormatConversionProvider
  278. protected int getFrameSize(AudioFormat.Encoding encoding, float sampleRate,
  279. int sampleSize, int channels, float frameRate, boolean bigEndian,
  280. int oldFrameSize) {
  281. if (encoding.equals(AudioFormat.Encoding.PCM_SIGNED)
  282. || encoding.equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
  283. return super.getFrameSize(encoding, sampleRate, sampleSize,
  284. channels, frameRate, bigEndian, oldFrameSize);
  285. }
  286. // return default frame rate for MPEG
  287. return MPEG_FRAME_RATE;
  288. }
  289. public static class EncodedMpegAudioInputStream extends
  290. TAsynchronousFilteredAudioInputStream {
  291. private InputStream pcmStream;
  292. private Lame encoder;
  293. private byte[] pcmBuffer;
  294. private byte[] encodedBuffer;
  295. public EncodedMpegAudioInputStream(AudioFormat targetFormat,
  296. AudioInputStream sourceStream) {
  297. super(targetFormat, -1);
  298. pcmStream = sourceStream;
  299. encoder = new Lame(sourceStream.getFormat(), targetFormat);
  300. this.format = encoder.getEffectiveFormat();
  301. pcmBuffer = new byte[encoder.getPCMBufferSize()];
  302. encodedBuffer = new byte[encoder.getMP3BufferSize()];
  303. }
  304. public void execute() {
  305. try {
  306. if (encoder == null) {
  307. if (TDebug.TraceAudioConverter) {
  308. TDebug.out("mp3 lame encoder is null (already at end of stream)");
  309. }
  310. getCircularBuffer().close();
  311. return;
  312. }
  313. int encodedBytes = 0;
  314. byte[] buffer = null;
  315. while (encodedBytes == 0 && encoder != null) {
  316. int readBytes = pcmStream.read(pcmBuffer);
  317. // what to do in case of readBytes==0 ?
  318. if (readBytes > 0) {
  319. encodedBytes = encoder.encodeBuffer(pcmBuffer, 0,
  320. readBytes, encodedBuffer);
  321. buffer = encodedBuffer;
  322. } else {
  323. // take the larger buffer for the remaining frame(s)
  324. buffer = encodedBuffer.length > pcmBuffer.length ? encodedBuffer
  325. : pcmBuffer;
  326. encodedBytes = encoder.encodeFinish(buffer);
  327. encoder.close();
  328. encoder = null;
  329. }
  330. }
  331. if (encodedBytes > 0) {
  332. getCircularBuffer().write(buffer, 0, encodedBytes);
  333. }
  334. if (encoder == null) {
  335. getCircularBuffer().close();
  336. }
  337. } catch (ArrayIndexOutOfBoundsException e) {
  338. if (TDebug.TraceAudioConverter || TDebug.TraceAllExceptions) {
  339. TDebug.out(e);
  340. }
  341. } catch (IOException e) {
  342. if (TDebug.TraceAudioConverter || TDebug.TraceAllExceptions) {
  343. TDebug.out(e);
  344. }
  345. }
  346. }
  347. public void close() throws IOException {
  348. super.close();
  349. pcmStream.close();
  350. if (encoder != null) {
  351. encoder.encodeFinish(null);
  352. encoder.close();
  353. encoder = null;
  354. }
  355. }
  356. }
  357. }
  358. /** * Mp3LameFormatConversionProvider.java ** */