/* * Copyright (c) 2008, 2009, 2010, 2011 Denis Tulskiy * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>. */ package com.tulskiy.musique.audio.formats.mp4; import com.beatofthedrum.alacdecoder.AlacContext; import com.beatofthedrum.alacdecoder.AlacUtils; import com.beatofthedrum.alacdecoder.DecodeResult; import com.tulskiy.musique.audio.Decoder; import com.tulskiy.musique.playlist.Track; import javax.sound.sampled.AudioFormat; /** * Author: Denis Tulskiy * Date: 4/2/11 */ public class ALACDecoder implements Decoder { private AlacContext alacContext; private AudioFormat audioFormat; public static final int destBufferSize = 1024 * 24 * 3; // 24kb buffer = 4096 frames = 1 alac sample (we support max 24bps) private int[] pDestBuffer; private int bps; private int totalSamples; @Override public boolean open(Track track) { alacContext = AlacUtils.AlacOpenFileInput(track.getTrackData().getFile().getAbsolutePath()); if (alacContext.error) { logger.warning("Error while opening alac file: " + alacContext.error_message); return false; } int channels = AlacUtils.AlacGetNumChannels(alacContext); totalSamples = AlacUtils.AlacGetNumSamples(alacContext); bps = AlacUtils.AlacGetBytesPerSample(alacContext); int sampleRate = AlacUtils.AlacGetSampleRate(alacContext); int bitps = AlacUtils.AlacGetBitsPerSample(alacContext); pDestBuffer = new int[destBufferSize]; audioFormat = new AudioFormat(sampleRate, bitps, channels, true, false); return true; } @Override public AudioFormat getAudioFormat() { return audioFormat; } @Override public void seekSample(long sample) { AlacUtils.AlacSetPosition(alacContext, sample); } @Override public int decode(byte[] buf) { int bytesUnpacked = AlacUtils.AlacUnpackSamples(alacContext, pDestBuffer); if (bytesUnpacked > 0) { formatSamples(bps, pDestBuffer, 0, buf, bytesUnpacked); return bytesUnpacked; } return -1; } private void formatSamples(int bps, int[] src, int offset, byte[] dst, int samcnt) { int temp; int destPos = 0; int srcPos = offset; switch (bps) { case 1: while (samcnt > 0) { dst[destPos] = (byte) (0x00FF & (src[srcPos] + 128)); destPos++; samcnt--; } break; case 2: while (samcnt > 0) { temp = src[srcPos++]; dst[destPos++] = (byte) temp; dst[destPos++] = (byte) (temp >>> 8); samcnt = samcnt - 2; } break; case 3: while (samcnt > 0) { dst[destPos] = (byte) src[srcPos]; destPos++; srcPos++; samcnt--; } break; } } @Override public void close() { if (alacContext != null) AlacUtils.AlacCloseFile(alacContext); } }