/** * Copyright (c) 2009-2011 SKRATCHDOT.COM * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * http://www.eclipse.org/legal/epl-v10.html * * Initial modeling finished using information provided by: * http://www.sonicspot.com/guide/wavefiles.html * * Contributors: * JEFF |:at:| SKRATCHDOT |:dot:| COM * * $Id$ */ package com.skratchdot.riff.wav.impl; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteOrder; import java.util.Collection; import java.util.HashMap; import java.util.Map; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.UnsupportedAudioFileException; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.EObjectImpl; import org.eclipse.emf.ecore.util.EObjectContainmentEList; import org.eclipse.emf.ecore.util.InternalEList; import com.skratchdot.riff.wav.Chunk; import com.skratchdot.riff.wav.ChunkData; import com.skratchdot.riff.wav.ChunkFormat; import com.skratchdot.riff.wav.ChunkTypeID; import com.skratchdot.riff.wav.CompressionCode; import com.skratchdot.riff.wav.ParseChunkException; import com.skratchdot.riff.wav.RIFFWave; import com.skratchdot.riff.wav.WavPackage; import com.skratchdot.riff.wav.util.ExtendedByteBuffer; import com.skratchdot.riff.wav.util.RiffWaveException; import com.skratchdot.riff.wav.util.WavUtil; /** * <!-- begin-user-doc --> * An implementation of the model object '<em><b>RIFF Wave</b></em>'. * <!-- end-user-doc --> * <p> * The following features are implemented: * <ul> * <li>{@link com.skratchdot.riff.wav.impl.RIFFWaveImpl#getChunks <em>Chunks</em>}</li> * <li>{@link com.skratchdot.riff.wav.impl.RIFFWaveImpl#getParseChunkExceptions <em>Parse Chunk Exceptions</em>}</li> * <li>{@link com.skratchdot.riff.wav.impl.RIFFWaveImpl#getSize <em>Size</em>}</li> * </ul> * </p> * * @generated */ public class RIFFWaveImpl extends EObjectImpl implements RIFFWave { /** * The cached value of the '{@link #getChunks() <em>Chunks</em>}' containment reference list. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getChunks() * @generated * @ordered */ protected EList<Chunk> chunks; /** * The cached value of the '{@link #getParseChunkExceptions() <em>Parse Chunk Exceptions</em>}' containment reference list. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getParseChunkExceptions() * @generated * @ordered */ protected EList<ParseChunkException> parseChunkExceptions; /** * The default value of the '{@link #getSize() <em>Size</em>}' attribute. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getSize() * @generated * @ordered */ protected static final long SIZE_EDEFAULT = 0L; /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected RIFFWaveImpl() { super(); } /** * @param file a valid RIFF Wave file * @throws RiffWaveException */ public RIFFWaveImpl(File file) throws RiffWaveException { try { ExtendedByteBuffer buf = new ExtendedByteBuffer(WavUtil.getBytesFromFile(file)); buf.order(ByteOrder.LITTLE_ENDIAN); this.init(buf); } catch (Exception e) { e.printStackTrace(); throw new RiffWaveException("The file was not a valid RIFF Wave file.\n"+e.getMessage(), e.getCause()); } } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override protected EClass eStaticClass() { return WavPackage.Literals.RIFF_WAVE; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public EList<Chunk> getChunks() { if (chunks == null) { chunks = new EObjectContainmentEList<Chunk>(Chunk.class, this, WavPackage.RIFF_WAVE__CHUNKS); } return chunks; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public EList<ParseChunkException> getParseChunkExceptions() { if (parseChunkExceptions == null) { parseChunkExceptions = new EObjectContainmentEList<ParseChunkException>(ParseChunkException.class, this, WavPackage.RIFF_WAVE__PARSE_CHUNK_EXCEPTIONS); } return parseChunkExceptions; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public long getSize() { long returnSize = 4; for(int i=0; i<this.getChunks().size(); i++) { returnSize += this.getChunks().get(i).getBlockAlignedSize() + 8; } return returnSize; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public EList<Chunk> getChunksByEClass(EClass eClass) { EList<Chunk> chunkList = new BasicEList<Chunk>(); TreeIterator<EObject> iter = this.eAllContents(); while (iter.hasNext()) { EObject next = iter.next(); if(next.eClass().equals(eClass) && next instanceof Chunk) { chunkList.add((Chunk) next); } } return chunkList; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public Chunk getFirstChunkByEClass(EClass eClass) { TreeIterator<EObject> iter = this.eAllContents(); while (iter.hasNext()) { EObject next = iter.next(); if(next.eClass().equals(eClass) && next instanceof Chunk) { return (Chunk)next; } } return null; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void init(ExtendedByteBuffer buf) throws RiffWaveException { // First read in the header info if(ChunkTypeID.get((int)buf.getUnsignedInt())!=ChunkTypeID.RIFF) throw new RiffWaveException("Invalid Header: missing RIFF"); if(buf.getUnsignedInt()!=buf.limit()-8) throw new RiffWaveException("Invalid Header: chunk data size"); if(ChunkTypeID.get((int)buf.getUnsignedInt())!=ChunkTypeID.WAVE) throw new RiffWaveException("Invalid Header: missing WAVE"); // loopPointer prevents an infinite loop if we try to parse a // chunk and the filePointer doesn't advance for some reason long loopPointer = 0; // Loop through file reading in chunks while(buf.position()<buf.limit() && buf.position()!=loopPointer) { // If the filePointer doesn't advance in this loop iteration, // then we'll break out of the loop loopPointer = buf.position(); // Grab the current chunk Chunk currentChunk = WavUtil.parseChunk(this, buf); // If we got a chunk, add it to our list if(currentChunk!=null) { this.getChunks().add(currentChunk); } // We need to block align buf.blockAlign(); } } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public AudioFileFormat toAudioFileFormat() throws UnsupportedAudioFileException { try { AudioFormat audioFormat = this.toAudioFormat(); ChunkData chunkData = (ChunkData) this.getFirstChunkByEClass(WavPackage.Literals.CHUNK_DATA); if(chunkData==null) { throw new UnsupportedAudioFileException("Could not find a data chunk"); } return new AudioFileFormat( AudioFileFormat.Type.WAVE, audioFormat, chunkData.getSampleDataOriginal().length/audioFormat.getFrameSize(), audioFormat.properties() ); } catch (Exception e) { throw new UnsupportedAudioFileException(e.getMessage()); } } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public AudioFormat toAudioFormat() throws UnsupportedAudioFileException { try { ChunkFormat chunkFormat = (ChunkFormat) this.getFirstChunkByEClass(WavPackage.Literals.CHUNK_FORMAT); if(chunkFormat==null) { throw new UnsupportedAudioFileException("Could not find a format chunk"); } else if(chunkFormat.getCompressionCode()==CompressionCode.COMPRESSION_CODE_1) { Map<String, Object> properties = new HashMap<String, Object>(); return new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, chunkFormat.getSampleRate(), chunkFormat.getSignificantBitsPerSample(), chunkFormat.getNumberOfChannels(), chunkFormat.getBlockAlign(), chunkFormat.getSampleRate(), false, properties ); } throw new UnsupportedAudioFileException("Not in PCM format."); } catch (Exception e) { throw new UnsupportedAudioFileException(e.getMessage()); } } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public AudioInputStream toAudioInputStream() throws UnsupportedAudioFileException { try { AudioFormat audioFormat = this.toAudioFormat(); ChunkData chunkData = (ChunkData) this.getFirstChunkByEClass(WavPackage.Literals.CHUNK_DATA); if(chunkData==null) { throw new UnsupportedAudioFileException("Could not find a data chunk"); } ByteArrayInputStream bais = new ByteArrayInputStream(chunkData.getSampleDataOriginal()); return new AudioInputStream( bais, audioFormat, chunkData.getSampleDataOriginal().length/audioFormat.getFrameSize() ); } catch (Exception e) { throw new UnsupportedAudioFileException(e.getMessage()); } } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public byte[] toByteArray() throws RiffWaveException { ExtendedByteBuffer buf = new ExtendedByteBuffer((int) this.getSize()+8); buf.order(ByteOrder.LITTLE_ENDIAN); buf.putUnsignedInt(ChunkTypeID.RIFF_VALUE); buf.putUnsignedInt(this.getSize()); buf.putUnsignedInt(ChunkTypeID.WAVE_VALUE); for(int i=0; i<this.getChunks().size(); i++) { Chunk currentChunk = this.getChunks().get(i); buf.putBytes(currentChunk.toByteArray()); buf.putBlockAlign(); } // confirm the size we wrote/calculated was correct if(this.getSize()!=buf.position()-8) { throw new RiffWaveException("Calculated incorrect chunk data size"); } return buf.array(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void write(File file) throws IOException, RiffWaveException { FileOutputStream fos = new FileOutputStream(file); fos.write(this.toByteArray()); fos.close(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) { switch (featureID) { case WavPackage.RIFF_WAVE__CHUNKS: return ((InternalEList<?>)getChunks()).basicRemove(otherEnd, msgs); case WavPackage.RIFF_WAVE__PARSE_CHUNK_EXCEPTIONS: return ((InternalEList<?>)getParseChunkExceptions()).basicRemove(otherEnd, msgs); } return super.eInverseRemove(otherEnd, featureID, msgs); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public Object eGet(int featureID, boolean resolve, boolean coreType) { switch (featureID) { case WavPackage.RIFF_WAVE__CHUNKS: return getChunks(); case WavPackage.RIFF_WAVE__PARSE_CHUNK_EXCEPTIONS: return getParseChunkExceptions(); case WavPackage.RIFF_WAVE__SIZE: return getSize(); } return super.eGet(featureID, resolve, coreType); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @SuppressWarnings("unchecked") @Override public void eSet(int featureID, Object newValue) { switch (featureID) { case WavPackage.RIFF_WAVE__CHUNKS: getChunks().clear(); getChunks().addAll((Collection<? extends Chunk>)newValue); return; case WavPackage.RIFF_WAVE__PARSE_CHUNK_EXCEPTIONS: getParseChunkExceptions().clear(); getParseChunkExceptions().addAll((Collection<? extends ParseChunkException>)newValue); return; } super.eSet(featureID, newValue); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public void eUnset(int featureID) { switch (featureID) { case WavPackage.RIFF_WAVE__CHUNKS: getChunks().clear(); return; case WavPackage.RIFF_WAVE__PARSE_CHUNK_EXCEPTIONS: getParseChunkExceptions().clear(); return; } super.eUnset(featureID); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public boolean eIsSet(int featureID) { switch (featureID) { case WavPackage.RIFF_WAVE__CHUNKS: return chunks != null && !chunks.isEmpty(); case WavPackage.RIFF_WAVE__PARSE_CHUNK_EXCEPTIONS: return parseChunkExceptions != null && !parseChunkExceptions.isEmpty(); case WavPackage.RIFF_WAVE__SIZE: return getSize() != SIZE_EDEFAULT; } return super.eIsSet(featureID); } } //RIFFWaveImpl