/* * 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.info; import davaguine.jmac.tools.ByteBuffer; import davaguine.jmac.tools.File; import davaguine.jmac.tools.IntegerPointer; import davaguine.jmac.tools.JMACException; import java.io.IOException; /** * Author: Dmitry Vaguine * Date: 07.05.2004 * Time: 14:10:50 */ public class WAVInputSource extends InputSource { // construction / destruction public WAVInputSource(File pIO, WaveFormat pwfeSource, IntegerPointer pTotalBlocks, IntegerPointer pHeaderBytes, IntegerPointer pTerminatingBytes) throws IOException { super(pIO, pwfeSource, pTotalBlocks, pHeaderBytes, pTerminatingBytes); m_bIsValid = false; if (pIO == null || pwfeSource == null) throw new JMACException("Bad Parameters"); m_spIO = pIO; m_bOwnsInputIO = false; AnalyzeSource(); // fill in the parameters pwfeSource = m_wfeSource; if (pTotalBlocks != null) pTotalBlocks.value = m_nDataBytes / m_wfeSource.nBlockAlign; if (pHeaderBytes != null) pHeaderBytes.value = m_nHeaderBytes; if (pTerminatingBytes != null) pTerminatingBytes.value = m_nTerminatingBytes; m_bIsValid = true; } public WAVInputSource(String pSourceName, WaveFormat pwfeSource, IntegerPointer pTotalBlocks, IntegerPointer pHeaderBytes, IntegerPointer pTerminatingBytes) throws IOException { super(pSourceName, pwfeSource, pTotalBlocks, pHeaderBytes, pTerminatingBytes); m_bIsValid = false; if (pSourceName == null || pwfeSource == null) throw new JMACException("Bad Parameters"); m_spIO = File.createFile(pSourceName, "r"); m_bOwnsInputIO = true; AnalyzeSource(); // fill in the parameters pwfeSource.wFormatTag = m_wfeSource.wFormatTag; pwfeSource.nChannels = m_wfeSource.nChannels; pwfeSource.nSamplesPerSec = m_wfeSource.nSamplesPerSec; pwfeSource.nAvgBytesPerSec = m_wfeSource.nAvgBytesPerSec; pwfeSource.nBlockAlign = m_wfeSource.nBlockAlign; pwfeSource.wBitsPerSample = m_wfeSource.wBitsPerSample; if (pTotalBlocks != null) pTotalBlocks.value = m_nDataBytes / m_wfeSource.nBlockAlign; if (pHeaderBytes != null) pHeaderBytes.value = m_nHeaderBytes; if (pTerminatingBytes != null) pTerminatingBytes.value = m_nTerminatingBytes; m_bIsValid = true; } public void Close() throws IOException { if (m_bIsValid && m_bOwnsInputIO && m_spIO != null) m_spIO.close(); m_spIO = null; } protected void finalize() { try { Close(); } catch (IOException e) { throw new JMACException("Error while closing input stream."); } } // get data public int GetData(ByteBuffer pBuffer, int nBlocks) throws IOException { if (!m_bIsValid) throw new JMACException("Undefined Error"); int nBytes = (m_wfeSource.nBlockAlign * nBlocks); int nBytesRead = m_spIO.read(pBuffer.getBytes(), pBuffer.getIndex(), nBytes); return nBytesRead / m_wfeSource.nBlockAlign; } // get header / terminating data public void GetHeaderData(byte[] pBuffer) throws IOException { if (!m_bIsValid) throw new JMACException("Undefined Error"); if (m_nHeaderBytes > 0) { long nOriginalFileLocation = m_spIO.getFilePointer(); m_spIO.seek(0); if (m_spIO.read(pBuffer, 0, m_nHeaderBytes) != m_nHeaderBytes) throw new JMACException("Undefined Error"); m_spIO.seek(nOriginalFileLocation); } } public void GetTerminatingData(byte[] pBuffer) throws IOException { if (!m_bIsValid) throw new JMACException("Undefined Error"); if (m_nTerminatingBytes > 0) { long nOriginalFileLocation = m_spIO.getFilePointer(); m_spIO.seek(m_spIO.length() - m_nTerminatingBytes); if (m_spIO.read(pBuffer, 0, m_nTerminatingBytes) != m_nTerminatingBytes) throw new JMACException("Undefined Error"); m_spIO.seek(nOriginalFileLocation); } } private void AnalyzeSource() throws IOException { // seek to the beginning (just in case) m_spIO.seek(0); // get the file size m_nFileBytes = (int) m_spIO.length(); // get the RIFF header int riffSignature = m_spIO.readInt(); int goalSignature = ('R' << 24) | ('I' << 16) | ('F' << 8) | ('F'); if (riffSignature != goalSignature) throw new JMACException("Invalid Input File"); m_spIO.readInt(); // read the data type header int dataTypeSignature = m_spIO.readInt(); goalSignature = ('W' << 24) | ('A' << 16) | ('V' << 8) | ('E'); // make sure it's the right data type if (dataTypeSignature != goalSignature) throw new JMACException("Invalid Input File"); // find the 'fmt ' chunk RiffChunkHeader RIFFChunkHeader = new RiffChunkHeader(); RIFFChunkHeader.read(m_spIO); goalSignature = (' ' << 24) | ('t' << 16) | ('m' << 8) | ('f'); while (RIFFChunkHeader.cChunkLabel != goalSignature) { // move the file pointer to the end of this chunk m_spIO.seek(m_spIO.getFilePointer() + RIFFChunkHeader.nChunkBytes); // check again for the data chunk RIFFChunkHeader.read(m_spIO); } // read the format info WaveFormat WAVFormatHeader = new WaveFormat(); WAVFormatHeader.readHeader(m_spIO); // error check the header to see if we support it if (WAVFormatHeader.wFormatTag != 1) throw new JMACException("Invalid Input File"); // copy the format information to the WAVEFORMATEX passed in WaveFormat.FillWaveFormatEx(m_wfeSource, WAVFormatHeader.nSamplesPerSec, WAVFormatHeader.wBitsPerSample, WAVFormatHeader.nChannels); // skip over any extra data in the header int nWAVFormatHeaderExtra = (int) (RIFFChunkHeader.nChunkBytes - WaveFormat.WAV_HEADER_SIZE); if (nWAVFormatHeaderExtra < 0) throw new JMACException("Invalid Input File"); else m_spIO.seek(m_spIO.getFilePointer() + nWAVFormatHeaderExtra); // find the data chunk RIFFChunkHeader.read(m_spIO); goalSignature = ('a' << 24) | ('t' << 16) | ('a' << 8) | ('d'); while (RIFFChunkHeader.cChunkLabel != goalSignature) { // move the file pointer to the end of this chunk m_spIO.seek(m_spIO.getFilePointer() + RIFFChunkHeader.nChunkBytes); // check again for the data chunk RIFFChunkHeader.read(m_spIO); } // we're at the data block m_nHeaderBytes = (int) m_spIO.getFilePointer(); m_nDataBytes = (int) RIFFChunkHeader.nChunkBytes; if (m_nDataBytes < 0) m_nDataBytes = m_nFileBytes - m_nHeaderBytes; // make sure the data bytes is a whole number of blocks if ((m_nDataBytes % m_wfeSource.nBlockAlign) != 0) throw new JMACException("Invalid Input File"); // calculate the terminating byts m_nTerminatingBytes = m_nFileBytes - m_nDataBytes - m_nHeaderBytes; } private File m_spIO; private boolean m_bOwnsInputIO; private WaveFormat m_wfeSource = new WaveFormat(); private int m_nHeaderBytes; private int m_nDataBytes; private int m_nTerminatingBytes; private int m_nFileBytes; private boolean m_bIsValid; }