/* * 21.04.2004 Original verion. davagin@udm.ru. *----------------------------------------------------------------------- * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *---------------------------------------------------------------------- */ package davaguine.jmac.encoder; import davaguine.jmac.info.InputSource; import davaguine.jmac.info.WaveFormat; import davaguine.jmac.tools.*; import java.io.IOException; /** * Author: Dmitry Vaguine * Date: 08.05.2004 * Time: 11:17:57 */ public class APECompress extends IAPECompress { public APECompress() { m_nBufferHead = 0; m_nBufferTail = 0; m_nBufferSize = 0; m_bBufferLocked = false; m_bOwnsOutputIO = false; m_pioOutput = null; m_spAPECompressCreate = new APECompressCreate(); m_pBuffer = null; } protected void finalize() { Kill(); } // start encoding public void Start(String pOutputFilename, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel, byte[] pHeaderData, int nHeaderBytes) throws IOException { m_pioOutput = File.createFile(pOutputFilename, "rw"); m_bOwnsOutputIO = true; m_spAPECompressCreate.Start(m_pioOutput, pwfeInput, nMaxAudioBytes, nCompressionLevel, pHeaderData, nHeaderBytes); m_nBufferSize = m_spAPECompressCreate.GetFullFrameBytes(); m_pBuffer = new byte[m_nBufferSize]; m_wfeInput = pwfeInput; } public void StartEx(File pioOutput, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel, byte[] pHeaderData, int nHeaderBytes) throws IOException { m_pioOutput = pioOutput; m_bOwnsOutputIO = false; m_spAPECompressCreate.Start(m_pioOutput, pwfeInput, nMaxAudioBytes, nCompressionLevel, pHeaderData, nHeaderBytes); m_nBufferSize = m_spAPECompressCreate.GetFullFrameBytes(); m_pBuffer = new byte[m_nBufferSize]; m_wfeInput = pwfeInput; } // add data / compress data // allows linear, immediate access to the buffer (fast) public int GetBufferBytesAvailable() { return m_nBufferSize - m_nBufferTail; } public void UnlockBuffer(int nBytesAdded, boolean bProcess) throws IOException { if (!m_bBufferLocked) throw new JMACException("Error Undefined"); m_nBufferTail += nBytesAdded; m_bBufferLocked = false; if (bProcess) ProcessBuffer(); } private ByteBuffer pBufferPointer = new ByteBuffer(); public ByteBuffer LockBuffer(IntegerPointer pBytesAvailable) { if (m_pBuffer == null) { return null; } if (m_bBufferLocked) return null; m_bBufferLocked = true; if (pBytesAvailable != null) pBytesAvailable.value = GetBufferBytesAvailable(); pBufferPointer.reset(m_pBuffer, m_nBufferTail); return pBufferPointer; } // slower, but easier than locking and unlocking (copies data) private IntegerPointer m_nAddDataBytesAvailable = new IntegerPointer(); public void AddData(byte[] pData, int nBytes) throws IOException { int nBytesDone = 0; while (nBytesDone < nBytes) { // lock the buffer m_nAddDataBytesAvailable.value = 0; ByteBuffer pBuffer = LockBuffer(m_nAddDataBytesAvailable); if (pBuffer == null || m_nAddDataBytesAvailable.value <= 0) throw new JMACException("Error Undefined"); // calculate how many bytes to copy and add that much to the buffer int nBytesToProcess = Math.min(m_nAddDataBytesAvailable.value, nBytes - nBytesDone); pBuffer.append(pData, nBytesDone, nBytesToProcess); // unlock the buffer (fail if not successful) UnlockBuffer(nBytesToProcess); // update our progress nBytesDone += nBytesToProcess; } } // use a CIO (input source) to add data private IntegerPointer m_nAddDataFromInputSourceBytesAvailavle = new IntegerPointer(); public int AddDataFromInputSource(InputSource pInputSource, int nMaxBytes) throws IOException { // error check the parameters if (pInputSource == null) throw new JMACException("Bad Parameters"); // initialize int pBytesAdded = 0; int nBytesRead = 0; // lock the buffer m_nAddDataFromInputSourceBytesAvailavle.value = 0; ByteBuffer pBuffer = LockBuffer(m_nAddDataFromInputSourceBytesAvailavle); // calculate the 'ideal' number of bytes int nIdealBytes = m_spAPECompressCreate.GetFullFrameBytes() - (m_nBufferTail - m_nBufferHead); System.out.println(nIdealBytes); if (nIdealBytes > 0) { // get the data int nBytesToAdd = m_nAddDataFromInputSourceBytesAvailavle.value; if (nMaxBytes > 0) { if (nBytesToAdd > nMaxBytes) nBytesToAdd = nMaxBytes; } if (nBytesToAdd > nIdealBytes) nBytesToAdd = nIdealBytes; // always make requests along block boundaries while ((nBytesToAdd % m_wfeInput.nBlockAlign) != 0) nBytesToAdd--; int nBlocksToAdd = nBytesToAdd / m_wfeInput.nBlockAlign; // get data int nBlocksAdded = pInputSource.GetData(pBuffer, nBlocksToAdd); nBytesRead = (nBlocksAdded * m_wfeInput.nBlockAlign); // store the bytes read pBytesAdded = nBytesRead; } // unlock the data and process UnlockBuffer(nBytesRead, true); return pBytesAdded; } // finish / kill public void Finish(byte[] pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) throws IOException { ProcessBuffer(true); m_spAPECompressCreate.Finish(pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes); } public void Kill() { if (m_pioOutput != null) { try { if (m_bOwnsOutputIO) m_pioOutput.close(); } catch (IOException e) { throw new JMACException("Error while closing output stream", e); } } m_pioOutput = null; } private void ProcessBuffer() throws IOException { ProcessBuffer(false); } private ByteArrayReader pByteReader = new ByteArrayReader(); private void ProcessBuffer(boolean bFinalize) throws IOException { if (m_pBuffer == null) throw new JMACException("Error Undefined"); // process as much as possible int nThreshold = (bFinalize) ? 0 : m_spAPECompressCreate.GetFullFrameBytes(); while ((m_nBufferTail - m_nBufferHead) >= nThreshold) { int nFrameBytes = Math.min(m_spAPECompressCreate.GetFullFrameBytes(), m_nBufferTail - m_nBufferHead); if (nFrameBytes == 0) break; pByteReader.reset(m_pBuffer, m_nBufferHead); m_spAPECompressCreate.EncodeFrame(pByteReader, nFrameBytes); m_nBufferHead += nFrameBytes; } // shift the buffer if (m_nBufferHead != 0) { int nBytesLeft = m_nBufferTail - m_nBufferHead; if (nBytesLeft != 0) System.arraycopy(m_pBuffer, m_nBufferHead, m_pBuffer, 0, nBytesLeft); m_nBufferTail -= m_nBufferHead; m_nBufferHead = 0; } } private APECompressCreate m_spAPECompressCreate; private int m_nBufferHead; private int m_nBufferTail; private int m_nBufferSize; private byte[] m_pBuffer; private boolean m_bBufferLocked; private File m_pioOutput; private boolean m_bOwnsOutputIO; private WaveFormat m_wfeInput; }