1   package org.daisy.pipeline.audio.impl;
2   
3   import java.io.File;
4   import java.io.FileOutputStream;
5   import java.io.InputStream;
6   import java.util.Map;
7   import java.util.Optional;
8   
9   import javax.sound.sampled.AudioFileFormat;
10  import javax.sound.sampled.AudioInputStream;
11  
12  import javazoom.jl.converter.Converter;
13  
14  import com.google.common.io.ByteStreams;
15  
16  import org.daisy.pipeline.audio.AudioDecoder;
17  import org.daisy.pipeline.audio.AudioDecoderService;
18  import org.daisy.pipeline.audio.AudioFileTypes;
19  
20  import org.osgi.service.component.annotations.Component;
21  
22  /*
23   * Note that it is in theory also possibly to support MP3 in SystemAudioDecoder, through the MP3SPI
24   * library (com.googlecode.soundlibs:mp3spi). When this library is on the class path,
25   * AudioSystem.getAudioInputStream() applied on an MP3 file will return an MPEG encoded audio
26   * stream, which we could then convert to a PCM encoded audio stream. Unfortunately this conversion
27   * does not work for some reason. The resulting PCM encoded stream contains all zeros. Another issue
28   * is that when MP3SPI is on the class path, AudioSystem.getAudioInputStream() may incorrectly read
29   * a WAV file as MPEGL1.
30   */
31  @Component(
32  	name = "jlayer-mp3-decoder",
33  	immediate = true,
34  	service = { AudioDecoderService.class }
35  )
36  public class JLayerMP3Decoder implements AudioDecoderService {
37  
38  	@Override
39  	public boolean supportsFileType(AudioFileFormat.Type fileType) {
40  		return AudioFileTypes.MP3.equals(fileType);
41  	}
42  
43  	@Override
44  	public Optional<AudioDecoder> newDecoder(Map<String,String> params) {
45  		if (instance == null)
46  			instance = new AudioDecoder() {
47  					@Override
48  					public AudioInputStream decode(InputStream input) throws Throwable {
49  						File tmpMp3 = File.createTempFile("mp3-to-wav-", ".mp3");
50  						ByteStreams.copy(input, new FileOutputStream(tmpMp3));
51  						try {
52  							return decode(tmpMp3);
53  						} finally {
54  							tmpMp3.delete();
55  						}
56  					}
57  					@Override
58  					public AudioInputStream decode(File inputFile) throws Throwable {
59  						File tmpWav = File.createTempFile("mp3-to-wav-", ".wav");
60  						tmpWav.deleteOnExit();
61  						new Converter().convert(inputFile.getAbsolutePath(), tmpWav.getAbsolutePath());
62  						try {
63  							return SystemAudioDecoder.getInstance().decode(tmpWav);
64  						} finally {
65  							tmpWav.delete();
66  						}
67  					}
68  				};
69  		return Optional.of(instance);
70  	}
71  
72  	private static AudioDecoder instance = null;
73  
74  }