38 | * If canSeek() returns false: The file will be written as normal, but the 39 | * headers will not be updated once the stream is closed. This means the FLAC 40 | * file will not contain a count of the total number of samples, nor the MD5 41 | * hash of the original input(used for verifying the data).46 | * @author Preston Lacey 47 | * 48 | * 49 | */ 50 | public interface FLACOutputStream { 51 | 52 | /** 53 | * Attempt to seek to the given position. 54 | * 55 | * @param pos target position. 56 | * @return current position after seek. 57 | */ 58 | public long seek(long pos) throws IOException; 59 | 60 | /** 61 | * Write the given number of bytes from a byte array. 62 | * 63 | * @param data array containing source bytes to write 64 | * @param offset index of source array to begin reading from. 65 | * @param count number of bytes to write. 66 | * @return number of bytes written. 67 | * @throws IOException IOException raised upon write error. 68 | */ 69 | public int write(byte[] data, int offset, int count) throws IOException; 70 | 71 | /** 72 | * Get the number of bytes that have been written in length. 73 | * This takes into account seeking to different portions. 74 | * 75 | * @return total writtne length of stream. 76 | */ 77 | public long size(); 78 | 79 | /** 80 | * Write a single byte to the stream. 81 | * 82 | * @param data byte to write. 83 | * @throws IOException IOException raised upon write error. 84 | */ 85 | public void write(byte data) throws IOException; 86 | 87 | /** 88 | * Test whether this object allows seeking. 89 | * 90 | * @return true if seeking is allowed, false otherwise. 91 | */ 92 | public boolean canSeek(); 93 | 94 | /** 95 | * Get current write position of this stream. If stream cannot seek, then 96 | * this will return 0; 97 | * 98 | * @return current write position. 99 | */ 100 | public long getPos(); 101 | } 102 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACStreamController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.util.concurrent.locks.ReentrantLock; 22 | import java.io.IOException; 23 | /** 24 | * 25 | * @author preston 26 | */ 27 | public class FLACStreamController { 28 | public static int DEBUG_LEV = 0; 29 | /** we set this to true while a flac stream has been opened and not 30 | * officially closed. Must use a streamLock to set and get this. Some actions 31 | * must not be taken while a stream is opened */ 32 | volatile boolean flacStreamIsOpen = false; 33 | 34 | private final ReentrantLock streamLock = new ReentrantLock(); 35 | 36 | /* Object to write results to. Must be set before opening stream */ 37 | private FLACOutputStream out = null; 38 | 39 | /* contains FLAC_id used in the flac stream header to signify FLAC format */ 40 | EncodedElement FLAC_id = FLACStreamIdentifier.getIdentifier(); 41 | 42 | /* total number of samples encoded to output. Used in stream header */ 43 | volatile long samplesInStream; 44 | 45 | /* next frame number to use */ 46 | volatile long nextFrameNumber = 0; 47 | 48 | /* position of header in output stream location(needed so we can update 49 | * the header info(md5, minBlockSize, etc), once encoding is done 50 | */ 51 | long streamHeaderPos = 0; 52 | 53 | /* minimum frame size seen so far. Used in the stream header */ 54 | volatile int minFrameSize = 0x7FFFFFFF; 55 | 56 | /* maximum frame size seen so far. Used in stream header */ 57 | volatile int maxFrameSize = 0; 58 | 59 | /* minimum block size used so far. Used in stream header */ 60 | volatile int minBlockSize = 0x7FFFFFFF; 61 | 62 | /* maximum block size used so far. Used in stream header */ 63 | volatile int maxBlockSize = 0; 64 | 65 | StreamConfiguration streamConfig = null; 66 | public FLACStreamController(FLACOutputStream fos, StreamConfiguration sc) { 67 | out = fos; 68 | streamConfig = new StreamConfiguration(sc); 69 | minFrameSize = 0x7FFFFFFF; 70 | maxFrameSize = 0; 71 | minBlockSize = 0x7FFFFFFF; 72 | maxBlockSize = 0; 73 | samplesInStream = 0; 74 | streamHeaderPos = 0; 75 | nextFrameNumber = 0; 76 | } 77 | public void setFLACOutputStream(FLACOutputStream fos) { 78 | streamLock.lock(); 79 | try { 80 | if(flacStreamIsOpen) 81 | throw new IllegalStateException("Cannot set new output stream while flac stream is open"); 82 | out = fos; 83 | }finally { 84 | streamLock.unlock(); 85 | } 86 | } 87 | public FLACOutputStream getFLACOutputStream() { return out; } 88 | 89 | 90 | /** 91 | * Close the current FLAC stream. Updates the stream header information. 92 | * If called on a closed stream, operation is undefined. Do not do this. 93 | */ 94 | public void closeFLACStream(byte[] md5Hash, StreamConfiguration streamConfig) 95 | throws IOException { 96 | //reset position in output stream to beginning. 97 | //re-write the updated stream info. 98 | streamLock.lock(); 99 | try { 100 | if(!flacStreamIsOpen) 101 | throw new IllegalStateException("Error. Cannot close a non-opened stream"); 102 | StreamConfiguration tempSC = new StreamConfiguration(streamConfig); 103 | tempSC.setMaxBlockSize(maxBlockSize); 104 | tempSC.setMinBlockSize(minBlockSize); 105 | EncodedElement streamInfo = MetadataBlockStreamInfo.getStreamInfo( 106 | tempSC, minFrameSize, maxFrameSize, samplesInStream, md5Hash); 107 | if(out.canSeek()) { 108 | out.seek(streamHeaderPos); 109 | this.writeDataToOutput(streamInfo); 110 | } 111 | flacStreamIsOpen = false; 112 | } finally { 113 | streamLock.unlock(); 114 | } 115 | } 116 | 117 | 118 | /** 119 | * Write the data stored in an EncodedElement to the output stream. 120 | * All data will be written along byte boundaries, but the elements in the 121 | * given list need not end on byte boundaries. If the data of an element 122 | * does not end on a byte boundary, then the space remaining in that last 123 | * byte will be used as an offset, and merged(using an "OR"), with the first 124 | * byte of the following element. 125 | * 126 | * @param data 127 | * @return 128 | * @throws IOException 129 | */ 130 | private int writeDataToOutput(EncodedElement data) throws IOException { 131 | 132 | int writtenBytes = 0; 133 | int offset = 0; 134 | EncodedElement current = data; 135 | int currentByte = 0; 136 | byte unfullByte = 0; 137 | byte[] eleData = null; 138 | int usableBits = 0; 139 | int lastByte = 0; 140 | while(current != null) { 141 | eleData = current.getData(); 142 | usableBits = current.getUsableBits(); 143 | currentByte = 0; 144 | //if offset is not zero, merge first byte with existing byte 145 | if(offset != 0) { 146 | unfullByte = (byte)(unfullByte | eleData[currentByte++]); 147 | out.write(unfullByte); 148 | } 149 | //write all full bytes of element. 150 | lastByte = usableBits/8; 151 | if(lastByte > 0) 152 | out.write(eleData, currentByte, lastByte-currentByte); 153 | //save non-full byte(if present), and set "offset" for next element. 154 | offset = usableBits %8; 155 | if(offset != 0) { 156 | unfullByte = eleData[lastByte]; 157 | } 158 | //update current. 159 | current = current.getNext(); 160 | } 161 | //if non-full byte remains. write. 162 | if(offset != 0) { 163 | out.write(eleData, lastByte, 1); 164 | } 165 | return writtenBytes; 166 | } 167 | 168 | public long incrementFrameNumber() { 169 | return nextFrameNumber++; 170 | } 171 | 172 | /** 173 | * Begin a new FLAC stream. Prior to calling this, you must have already 174 | * set the StreamConfiguration and the output stream, both of which must not 175 | * change until encoding is finished and the stream is closed. If this 176 | * FLACEncoder object has already been used to encode a stream, unencoded 177 | * samples may still be stored. Use clear() to dump them prior to calling 178 | * this method(if clear() not called, and samples are instead retained, the 179 | * StreamConfiguration must NOT have changed from the prior stream. 180 | * 181 | * @throws IOException if there is an error writing the headers to output. 182 | */ 183 | public void openFLACStream() throws IOException { 184 | streamLock.lock(); 185 | try { 186 | //reset all data. 187 | reset(); 188 | flacStreamIsOpen = true; 189 | //write FLAC stream identifier 190 | out.write(FLAC_id.getData(), 0, FLAC_id.getUsableBits()/8); 191 | //write stream headers. These must be updated at close of stream 192 | byte[] md5Hash = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//blank hash. Don't know it yet. 193 | EncodedElement streamInfo = MetadataBlockStreamInfo.getStreamInfo( 194 | streamConfig, minFrameSize, maxFrameSize, samplesInStream, md5Hash); 195 | //mark stream info location(so we can return to it and re-write headers, 196 | // assuming stream is seekable. Then write header. 197 | int size = streamInfo.getUsableBits()/8; 198 | EncodedElement metadataBlockHeader = 199 | MetadataBlockHeader.getMetadataBlockHeader(false, 200 | MetadataBlockHeader.MetadataBlockType.STREAMINFO, size); 201 | this.writeDataToOutput(metadataBlockHeader); 202 | streamHeaderPos = out.getPos(); 203 | out.write(streamInfo.getData(), 0, size); 204 | writePaddingToFoolJFlac(); 205 | }finally { 206 | streamLock.unlock(); 207 | } 208 | } 209 | private void reset() { 210 | minFrameSize = 0x7FFFFFFF; 211 | maxFrameSize = 0; 212 | minBlockSize = 0x7FFFFFFF; 213 | maxBlockSize = 0; 214 | samplesInStream = 0; 215 | streamHeaderPos = 0; 216 | nextFrameNumber = 0; 217 | } 218 | private void writePaddingToFoolJFlac() throws IOException { 219 | int size = 40; 220 | byte[] padding = new byte[size]; 221 | EncodedElement metadataBlockHeader = 222 | MetadataBlockHeader.getMetadataBlockHeader(true, 223 | MetadataBlockHeader.MetadataBlockType.PADDING, 40); 224 | this.writeDataToOutput(metadataBlockHeader); 225 | out.write(padding, 0,size); 226 | } 227 | 228 | public void writeBlock(BlockEncodeRequest ber) throws IOException { 229 | if(!flacStreamIsOpen) 230 | throw new IllegalStateException("Cannot write on a non-opened stream"); 231 | writeDataToOutput(ber.result.getNext()); 232 | //update encodedCount and count, and blocks, MD5 233 | if(ber.count != ber.encodedSamples) { 234 | System.err.println("Error encoding frame number: "+ 235 | ber.frameNumber+", FLAC stream potentially invalid"); 236 | } 237 | samplesInStream += ber.encodedSamples; 238 | if(ber.encodedSamples > maxBlockSize) 239 | maxBlockSize = ber.encodedSamples; 240 | if(ber.encodedSamples < minBlockSize) 241 | minBlockSize = ber.encodedSamples; 242 | int frameSize = ber.result.getTotalBits()/8; 243 | if(frameSize > maxFrameSize) maxFrameSize = frameSize; 244 | if(frameSize < minFrameSize) minFrameSize = frameSize; 245 | } 246 | 247 | } 248 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACStreamIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * Provides the stream identifier used at beginning of flac streams. 24 | * @author Preston Lacey 25 | */ 26 | public class FLACStreamIdentifier { 27 | static final byte streamMarkerByte1 = 0x66; 28 | static final byte streamMarkerByte2 = 0x4c; 29 | static final byte streamMarkerByte3 = 0x61; 30 | static final byte streamMarkerByte4 = 0x43; 31 | static final byte[] marker = { 32 | streamMarkerByte1, 33 | streamMarkerByte2, 34 | streamMarkerByte3, 35 | streamMarkerByte4, 36 | }; 37 | 38 | /** 39 | * Get an EncodedElement containing the marker(which is itself in a byte 40 | * array). 41 | * @return EncodedElement containing the marker. 42 | */ 43 | public static EncodedElement getIdentifier() { 44 | EncodedElement ele = new EncodedElement(); 45 | ele.setData(marker.clone()); 46 | ele.setUsableBits(32); 47 | return ele; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLACStreamOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.io.OutputStream; 22 | import java.io.IOException; 23 | import java.io.Closeable; 24 | /** 25 | * This class provides basic OutputStream support for writing from a FLACEncoder. 26 | * 27 | * @author Preston Lacey 28 | */ 29 | public class FLACStreamOutputStream implements FLACOutputStream,Closeable { 30 | 31 | OutputStream out = null; 32 | long size = 0; 33 | boolean valid; 34 | 35 | /** 36 | * Constructor. Create a FLACStreamOutputStream using the given OutputStream. 37 | * @param out OutputStream to write the FLAC stream to. 38 | */ 39 | public FLACStreamOutputStream(OutputStream out) throws IOException { 40 | this.out = out; 41 | size = 0; 42 | } 43 | 44 | /** 45 | * Attempt to seek to the given location within this stream. It is not 46 | * guaranteed that all implementations can or will support seeking. Use the 47 | * method canSeek() 48 | * 49 | * @param pos target position to seek to. 50 | * @return current position after seek attempt. 51 | */ 52 | public long seek(long pos) { 53 | throw new UnsupportedOperationException("seek(long) is not supported on by FLACStreamOutputStream"); 54 | } 55 | 56 | /** 57 | * Write a byte to this stream. 58 | * @param data byte to write. 59 | * @throws IOException IOException will be raised if an error occurred while 60 | * writing. 61 | */ 62 | public void write(byte data) throws IOException { 63 | out.write(data); 64 | size++; 65 | } 66 | /** 67 | * Write the given number of bytes from the byte array. Return number of 68 | * bytes written. 69 | * @param data array containing bytes to be written. 70 | * @param offset start index of array to begin reading from. 71 | * @param count number of bytes to write. 72 | * @return number of bytes written. 73 | * @throws IOException IOException upon a write error. 74 | */ 75 | public int write(byte[] data, int offset, int count) throws IOException { 76 | int result = count; 77 | out.write(data,offset,count); 78 | size += count; 79 | return result; 80 | } 81 | 82 | /** 83 | * Get the number of bytes that have been written by this object. 84 | * @return total length written. 85 | */ 86 | public long size() { 87 | return size; 88 | } 89 | 90 | /** 91 | * Test whether this stream is seekable. 92 | * @return true if stream is seekable, false otherwise 93 | */ 94 | public boolean canSeek() { 95 | return false; 96 | } 97 | 98 | /** 99 | * Get the current write position of this stream. If this stream cannot seek, 100 | * this will return 0; 101 | * @return current write position. 102 | */ 103 | public long getPos() { 104 | return 0; 105 | } 106 | 107 | /** 108 | * Close OutputStream owned by this object. 109 | * @throws IOException 110 | */ 111 | public void close() throws IOException { 112 | out.close(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FLAC_MD5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | import java.security.MessageDigest; 23 | 24 | 25 | /** 26 | * 27 | * @author preston 28 | */ 29 | public class FLAC_MD5 { 30 | private MessageDigest md = null; 31 | private byte[] _dataMD5 = null; 32 | 33 | public FLAC_MD5() throws java.security.NoSuchAlgorithmException { 34 | md = MessageDigest.getInstance("md5"); 35 | } 36 | 37 | public MessageDigest getMD() { 38 | return md; 39 | } 40 | 41 | /** 42 | * Add samples to the MD5 hash. 43 | * CURRENTLY ONLY MAY WORK FOR: sample sizes which are divisible by 8. Need 44 | * to create some audio to test with. 45 | * @param samples 46 | * @param count 47 | * @param channels 48 | */ 49 | public void addSamplesToMD5(int[] samples, int count, int channels, 50 | int sampleSize) { 51 | int bytesPerSample = sampleSize/8; 52 | if(sampleSize%8 != 0) 53 | bytesPerSample++; 54 | if(_dataMD5 == null || _dataMD5.length < count*bytesPerSample*channels) { 55 | _dataMD5 = new byte[count*bytesPerSample*channels]; 56 | } 57 | byte[] dataMD5 = _dataMD5; 58 | splitSamplesToBytes(samples, count*channels, bytesPerSample, dataMD5); 59 | md.update(dataMD5, 0, count*bytesPerSample*channels); 60 | } 61 | 62 | /* Split Samples to bytes(for sending to MD5) 63 | * CURRENTLY ONLY MAY WORK FOR: sample sizes which are divisible by 8. Need 64 | * to create some audio to test with.*/ 65 | private static final void splitSamplesToBytes(int[] samples, int totalSamples, 66 | int bytesPerSample, byte[] dataMD5) { 67 | int destIndexBase = 0; 68 | int i = 0; 69 | 70 | switch(bytesPerSample) { 71 | case 3: 72 | for(; i < totalSamples; i++) { 73 | dataMD5[destIndexBase++] = (byte)(samples[i]); 74 | dataMD5[destIndexBase++] = (byte)(samples[i] >> 8); 75 | dataMD5[destIndexBase++] = (byte)(samples[i] >> 16); 76 | } 77 | break; 78 | case 2: 79 | for(; i < totalSamples; i++) { 80 | dataMD5[destIndexBase++] = (byte)(samples[i]); 81 | dataMD5[destIndexBase++] = (byte)(samples[i] >> 8); 82 | } 83 | break; 84 | case 1: 85 | for(; i < totalSamples; i++) { 86 | dataMD5[i] = (byte)samples[i]; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FrameHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | 23 | /** 24 | * This class is used to generate a Frame Header for a FLAC Frame. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class FrameHeader { 29 | 30 | /** For Debugging: Higher level equals more debug statements */ 31 | public static int DEBUG_LEV = 0; 32 | 33 | private static final int definedBlockSizes[] = { 34 | -1, 35 | 192, 36 | 576, 37 | 1152, 38 | 2304, 39 | 4608, 40 | -1, 41 | -1, 42 | 256, 43 | 512, 44 | 1024, 45 | 2048, 46 | 4096, 47 | 8192, 48 | 16384, 49 | 32768 50 | }; 51 | 52 | private static final int definedSampleRates[] = { 53 | 0, 54 | 88200, 55 | 176400, 56 | 192000, 57 | 8000, 58 | 16000, 59 | 22050, 60 | 24000, 61 | 32000, 62 | 44100, 63 | 48000, 64 | 96000, 65 | -1, 66 | -1, 67 | -1, 68 | -1 69 | }; 70 | 71 | /** Maximum size a header can be according to FLAC specification. */ 72 | public static final int MAX_HEADER_SIZE = 128;//in bytes 73 | 74 | /** Synchronization code used at beginning of frame(low-order 14 bits 75 | * used) */ 76 | public static final short syncCode = 0x3FFE;//14 bits used 77 | 78 | static final byte reserved = 0;//1 bit used; 0 mandatory value 79 | 80 | byte blockingStrategy = 0;//1 bit used; 0=fixed-blocksize. 1=variable 81 | byte blockSize = 0xC;//4 bits used; see format docs 82 | byte sampleRate = 4;//0;//4 bits used; see format docs 83 | //byte channelAssignment = 0;//4 bits used; see format docs 84 | byte sampleSize = 4;//3 bits used; see format docs 85 | 86 | static final byte reserved2 = 0;//1 bit used; 0 mandatory value 87 | long frameNumber = 0;//8-56 bits used; UTF-8 coded sample number 88 | int blockSizeMod = 0;//if(blocksize bits == 011x) 8/16 bit (blocksize-1) 89 | int SampleRateMod = 0;//if(sample rate bits == 11xx) 8/16 bit sample rate 90 | byte crc8 = 0; 91 | CRC8 crcCalculator; 92 | 93 | /** 94 | * Constructor creates a new FrameHeader object which is ready to 95 | * generate headers. We can't use static functions to do this, since the 96 | * process uses a CRC8 object which must be instantiated. 97 | * 98 | */ 99 | public FrameHeader() { 100 | crcCalculator = new CRC8(); 101 | } 102 | 103 | /** 104 | * Create the header for a frame with the given parameters. Header data is 105 | * stored out to an EncodedElement, in the proper form for a FLAC stream. 106 | * 107 | * @param fixBlock True to use a fixed block size, false to use variable. At 108 | * this time, this *must* be set to True, as variable block size is 109 | * not yet implemented. 110 | * @param blockSize Block Size of this frame. 111 | * @param sampleRate Sample rate of this frame. 112 | * @param channelAssign Channel assignment used in this frame's encoding. 113 | * See EncodingConfiguration class documentation for 114 | * more information. 115 | * @param sampleSize Bits per sample. 116 | * @param frameNumber For fixed block-size encodings, this is the frame-number 117 | * starting at zero and incrementing by one. For variable 118 | * block encodings, this is the sample number of the 119 | * first sample in the frame. 120 | * @param channelCount Number of channels in the stream. 121 | * @return EncodedElement where the header is saved to. 122 | */ 123 | public EncodedElement createHeader(boolean fixBlock, int blockSize, 124 | int sampleRate, EncodingConfiguration.ChannelConfig channelAssign, 125 | int sampleSize, long frameNumber, int channelCount, EncodedElement result) { 126 | if(DEBUG_LEV > 0 ) 127 | System.err.println("FrameHeader::createHeader : Begin"); 128 | 129 | //EncodedElement result = new EncodedElement(); 130 | boolean useEndBlockSize = false; 131 | boolean useEndSampleRate = false; 132 | result.clear(MAX_HEADER_SIZE,0); 133 | int blockingStrat = (fixBlock)? 0:1; 134 | byte[] encodedFrameNumber = UTF8Modified.convertToExtendedUTF8(frameNumber); 135 | //set block size bits 136 | byte encodedBlockSize = encodeBlockSize(blockSize); 137 | if(encodedBlockSize == 0x6 || encodedBlockSize == 0x7) 138 | useEndBlockSize = true; 139 | 140 | //set sample rate bits 141 | byte encodedSampleRate = encodeSampleRate(sampleRate); 142 | if(encodedSampleRate >= 12 && encodedSampleRate <= 14) 143 | useEndSampleRate = true; 144 | 145 | //set channelAssignment bits 146 | int channelAssignment = 0; 147 | if(channelAssign == EncodingConfiguration.ChannelConfig.INDEPENDENT) 148 | channelAssignment = channelCount-1; 149 | else if(channelAssign == EncodingConfiguration.ChannelConfig.LEFT_SIDE) 150 | channelAssignment = 8; 151 | else if(channelAssign == EncodingConfiguration.ChannelConfig.RIGHT_SIDE) 152 | channelAssignment = 9; 153 | else if(channelAssign == EncodingConfiguration.ChannelConfig.MID_SIDE) 154 | channelAssignment = 10; 155 | 156 | //set sample size bits 157 | byte encodedSampleSize = 0; 158 | switch(sampleSize) { 159 | case 8: encodedSampleSize = 0x1; break; 160 | case 12: encodedSampleSize = 0x2; break; 161 | case 16: encodedSampleSize = 0x4; break; 162 | case 20: encodedSampleSize = 0x5; break; 163 | case 24: encodedSampleSize = 0x6; break; 164 | default: encodedSampleSize = 0x0; 165 | } 166 | 167 | result.addInt(syncCode,14); 168 | result.addInt(reserved, 1); 169 | result.addInt(blockingStrat, 1); 170 | result.addInt(encodedBlockSize, 4); 171 | result.addInt(encodedSampleRate, 4); 172 | result.addInt(channelAssignment, 4); 173 | result.addInt(encodedSampleSize, 3); 174 | result.addInt(reserved2, 1); 175 | 176 | for(int i = 0; i < encodedFrameNumber.length; i++) { 177 | result.addInt(encodedFrameNumber[i], 8); 178 | } 179 | 180 | //write blockSize if needed(two formats possible) 181 | if(useEndBlockSize) { 182 | if(encodedBlockSize == 0x6) { 183 | result.addInt(blockSize-1, 8); 184 | } 185 | else { 186 | result.addInt(blockSize-1, 16); 187 | } 188 | } 189 | //write sampleRate if needed(three formats possible) 190 | if(useEndSampleRate) { 191 | switch(encodedSampleRate) { 192 | case 0xC: 193 | result.addInt(sampleRate/1000, 8); 194 | break; 195 | case 0xD: 196 | result.addInt(sampleRate, 16); 197 | break; 198 | case 0xE: 199 | result.addInt(sampleRate/10, 16); 200 | break; 201 | } 202 | } 203 | if(DEBUG_LEV > 20 ) 204 | System.err.println("FrameHeader::createHeader : pre-CRC"); 205 | crcCalculator.reset(); 206 | crcCalculator.updateCRC8(result.getData(), 0, result.getTotalBits()/8); 207 | crc8 = crcCalculator.checksum(); 208 | if(DEBUG_LEV > 20 ) 209 | System.err.println("FrameHeader::createHeader : post-CRC"); 210 | result.addInt(crc8, 8); 211 | 212 | if(DEBUG_LEV > 0 ) 213 | System.err.println("FrameHeader::createHeader : End"); 214 | return result; 215 | } 216 | 217 | /** 218 | * Given a block size, select the proper bit settings to use according to 219 | * the FLAC stream. 220 | * @param blockSize 221 | * @return 222 | */ 223 | private static byte encodeBlockSize(int blockSize) { 224 | if(DEBUG_LEV > 0 ) 225 | System.err.println("FrameHeader::encodeBlockSize : Begin"); 226 | byte value = 0; 227 | int i; 228 | for(i = 0; i < definedBlockSizes.length; i++) { 229 | if(blockSize == definedBlockSizes[i]) { 230 | value = (byte)i; 231 | break; 232 | } 233 | } 234 | if(i >= definedBlockSizes.length) { 235 | if(blockSize <= 255) 236 | value = 0x6; 237 | else 238 | value = 0x7; 239 | } 240 | 241 | if(DEBUG_LEV > 0 ) 242 | System.err.println("FrameHeader::encodeBlockSize : End"); 243 | 244 | return value; 245 | } 246 | 247 | 248 | private static byte encodeSampleRate(int sampleRate) { 249 | if(DEBUG_LEV > 0 ) 250 | System.err.println("FrameHeader::encodeSampleRate : Begin"); 251 | byte value = 0; 252 | int i; 253 | for(i = 0; i < definedSampleRates.length; i++) { 254 | if(sampleRate == definedSampleRates[i]) { 255 | value = (byte)i; 256 | break; 257 | } 258 | } 259 | if(i >= definedSampleRates.length) { 260 | if(sampleRate % 1000 == 0 && sampleRate < 256000) 261 | value = 0xC; 262 | else if(sampleRate < 65536) 263 | value = 0xD; 264 | else if(sampleRate % 10 == 0 && sampleRate <= 655350) 265 | value = 0xE; 266 | else 267 | value = 0x0; 268 | } 269 | if(DEBUG_LEV > 0 ) 270 | System.err.println("FrameHeader::encodeSampleRate : End"); 271 | 272 | return value; 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/FrameThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | import java.util.concurrent.locks.ReentrantLock; 22 | 23 | /** 24 | * The FrameThread class provides threading support for a Frame object, allowing 25 | * multi-threaded encodings of FLAC frames. It's job is to repeatedly get a 26 | * BlockEncodeRequest from a BlockThreadManager, and encode it. 27 | * 28 | * @author Preston Lacey 29 | */ 30 | public class FrameThread implements Runnable { 31 | Frame frame = null; 32 | ReentrantLock runLock = null; 33 | BlockThreadManager manager = null; 34 | /** 35 | * Constructor. Private to prevent it's use, as a Frame must be provided for 36 | * this FrameThread to be of any use. 37 | */ 38 | private FrameThread() {} 39 | 40 | /** 41 | * Constructor. Sets the Frame object that this FrameThread will use for 42 | * encodings. 43 | * 44 | * @param f Frame object to use for encoding. 45 | * @param manager BlockThreadManager to use as the BlockEncodeRequest source 46 | * and destination. 47 | */ 48 | public FrameThread(Frame f, BlockThreadManager manager) { 49 | super(); 50 | if(f == null) 51 | System.err.println("Frame is null. Error."); 52 | frame = f; 53 | runLock = new ReentrantLock(); 54 | this.manager = manager; 55 | } 56 | 57 | /** 58 | * Run method. This FrameThread will get a BlockEncodeRequest from the 59 | * BlockThreadManager, encode the block, return it to the manager, then 60 | * repeat. If no BlockEncodeRequest is available, or if it recieves a 61 | * request with the "frameNumber" field set to a negative value, it will 62 | * break the loop and end, notifying the manager it has ended. 63 | * 64 | */ 65 | public void run() { 66 | boolean process = true; 67 | synchronized(this) { 68 | BlockEncodeRequest ber = manager.getWaitingRequest(); 69 | if(ber != null && ber.frameNumber < 0) 70 | ber = null; 71 | while(ber != null && process) { 72 | if(ber.frameNumber < 0) { 73 | process = false; 74 | } 75 | else {//get available BlockEncodeRequest from manager 76 | ber.encodedSamples = frame.encodeSamples(ber.samples, ber.count, 77 | ber.start, ber.skip, ber.result, ber.frameNumber); 78 | ber.valid = true; 79 | manager.returnFinishedRequest(ber); 80 | ber = manager.getWaitingRequest(); 81 | } 82 | } 83 | manager.notifyFrameThreadExit(this); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/LPC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * This class is used to calculate LPC Coefficients for a FLAC stream. 24 | * 25 | * @author Preston Lacey 26 | */ 27 | public class LPC { 28 | /** The error calculated by the LPC algorithm */ 29 | protected double rawError; 30 | /** The coefficients as calculated by the LPC algorithm */ 31 | protected double[] rawCoefficients; 32 | private double[] tempCoefficients; 33 | /** The order of this LPC calculation */ 34 | protected int order; 35 | static int[] tempSum = null; 36 | /** 37 | * Constructor creates an LPC object of the given order. 38 | * @param order Order for this LPC calculation. 39 | */ 40 | public LPC(int order) { 41 | this.order = order; 42 | rawError = 0; 43 | rawCoefficients = new double[order+1]; 44 | tempCoefficients = new double[order+1]; 45 | } 46 | /** 47 | * Get this LPC object's order 48 | * @return order used for this LPC calculation. 49 | */ 50 | public int getOrder () { return order; } 51 | /** 52 | * Get the error for this LPC calculation 53 | * @return lpc error 54 | */ 55 | public double getError() { return rawError; } 56 | 57 | /** 58 | * Get the calculated LPC Coefficients as an array. 59 | * @return lpc coefficients in an array. 60 | */ 61 | public double[] getCoefficients() { return rawCoefficients; } 62 | 63 | /** 64 | * Calculate an LPC using the given Auto-correlation data. Static method 65 | * used since this is slightly faster than a more strictly object-oriented 66 | * approach. 67 | * 68 | * @param lpc LPC to calculate 69 | * @param R Autocorrelation data to use 70 | */ 71 | public static void calculate(LPC lpc, long[] R) { 72 | int coeffCount = lpc.order; 73 | 74 | //calculate first iteration directly 75 | double[] A = lpc.rawCoefficients; 76 | for(int i = 0; i < coeffCount+1; i++) A[i] = 0.0; 77 | A[0] = 1; 78 | double E = R[0]; 79 | 80 | //calculate remaining iterations 81 | 82 | if(R[0] == 0) { 83 | for(int i = 0; i < coeffCount+1; i++) 84 | A[i] = 0.0; 85 | } 86 | else { 87 | double[] ATemp = lpc.tempCoefficients; 88 | for(int i = 0; i < coeffCount+1; i++) ATemp[i] = 0.0; 89 | 90 | for(int k = 0; k < coeffCount; k++) { 91 | double lambda = 0.0; 92 | double temp = 0; 93 | for(int j = 0; j <= k; j++) { 94 | temp += A[j]*R[k+1-j]; 95 | } 96 | lambda = -temp/E; 97 | 98 | for(int i = 0; i <= k+1; i++) { 99 | ATemp[i] = A[i]+lambda*A[k+1-i]; 100 | } 101 | System.arraycopy(ATemp, 0, A, 0, coeffCount+1); 102 | E = (1-lambda*lambda)*E; 103 | } 104 | } 105 | lpc.rawError = E; 106 | } 107 | 108 | /** 109 | * Calculate an LPC using a prior order LPC's values to save calculations. 110 | * 111 | * @param lpc LPC to calculate 112 | * @param R Auto-correlation data to use. 113 | * @param priorLPC Prior order LPC to use(may be any order lower than our 114 | * target LPC) 115 | * 116 | */ 117 | public static void calculateFromPrior(LPC lpc, long[] R, LPC priorLPC) { 118 | int coeffCount = lpc.order; 119 | 120 | //calculate first iteration directly 121 | double[] A = lpc.rawCoefficients; 122 | for(int i = 0; i < coeffCount+1; i++) A[i] = 0.0; 123 | A[0] = 1; 124 | double E = R[0]; 125 | int startIter = 0; 126 | if(priorLPC != null && priorLPC.order < lpc.order) { 127 | startIter = priorLPC.order; 128 | E = priorLPC.rawError; 129 | System.arraycopy(priorLPC.rawCoefficients, 0, A, 0, startIter+1); 130 | } 131 | //calculate remaining iterations 132 | if(R[0] == 0) { 133 | for(int i = 0; i < coeffCount+1; i++) 134 | A[i] = 0.0; 135 | } 136 | else { 137 | double[] ATemp = lpc.tempCoefficients; 138 | for(int i = 0; i < coeffCount+1; i++) ATemp[i] = 0.0; 139 | 140 | for(int k = startIter; k < coeffCount; k++) { 141 | double lambda = 0.0; 142 | double temp = 0.0; 143 | for(int j = 0; j <= k; j++) { 144 | temp -= A[j]*R[k-j+1]; 145 | } 146 | lambda = temp/E; 147 | 148 | for(int i = 0; i <= k+1; i++) { 149 | ATemp[i] = A[i]+lambda*A[k+1-i]; 150 | } 151 | System.arraycopy(ATemp, 0, A, 0, coeffCount+1); 152 | E = (1-lambda*lambda)*E; 153 | } 154 | } 155 | lpc.rawError = E; 156 | } 157 | 158 | /** 159 | * Create auto-correlation coefficients(up to a maxOrder of 32). 160 | * @param R Array to put results in. 161 | * @param samples Samples to calculate the auto-correlation for. 162 | * @param count number of samples to use 163 | * @param start index of samples array to start at 164 | * @param increment number of indices to increment between valid samples(for 165 | * interleaved arrays) 166 | * @param maxOrder maximum order to calculate. 167 | */ 168 | public static void createAutoCorrelation(long[] R, int []samples, int count, 169 | int start, int increment, int maxOrder) { 170 | if(increment == 1 && start == 0) { 171 | for(int i = 0; i <= maxOrder; i++) { 172 | R[i] = 0; 173 | long temp = 0; 174 | for(int j = 0; j < count-i; j++) { 175 | temp += (long)samples[j]*(long)samples[j+i]; 176 | } 177 | R[i] += temp; 178 | } 179 | } 180 | else { 181 | for(int i = 0; i <= maxOrder; i++) { 182 | R[i] = 0; 183 | int baseIndex = increment*i; 184 | long temp = 0; 185 | int innerLimit = (count-i)*increment; 186 | for(int j = start; j < innerLimit; j+=increment) { 187 | temp += (long)samples[j]*(long)samples[j+baseIndex]; 188 | } 189 | R[i] += temp; 190 | } 191 | } 192 | } 193 | 194 | /** 195 | * Apply a window function to sample data 196 | * @param samples Samples to apply window to. Values in this array are left 197 | * unaltered. 198 | * @param count number of samples to use 199 | * @param start index of samples array to start at 200 | * @param increment number of indices to increment between valid samples(for 201 | * interleaved arrays) 202 | * @param windowedSamples array containing windowed values. Return values 203 | * are packed(increment of one). 204 | * 205 | */ 206 | public static void window(int[] samples, int count, int start, int increment, 207 | int[] windowedSamples) { 208 | int[] values = windowedSamples; 209 | int loopCount = 0; 210 | float halfway = count/2.0f; 211 | float hth = halfway*halfway; 212 | float windowCount = -halfway; 213 | int limit = count*increment+start; 214 | for(int i = start; i < limit; i+=increment) { 215 | //float innerCount = (windowCount < 0) ? -windowCount:windowCount; 216 | float innerCountSquared = windowCount*windowCount; 217 | windowCount++; 218 | //double val = 1.0-(double)(innerCount/halfway); 219 | float val = 1.0f-( innerCountSquared/hth ); 220 | double temp = ((double)samples[i])*val; 221 | temp = (temp >0) ? temp+0.5:temp-0.5; 222 | values[loopCount++] = (int)temp; 223 | } 224 | } 225 | 226 | } 227 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/MetadataBlockHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * The MetadataBlockHeader class is used to creat FLAC compliant Metadata Block 24 | * Headers. See the FLAC specification for more information. 25 | * 26 | * @author Preston Lacey 27 | */ 28 | public class MetadataBlockHeader { 29 | 30 | //boolean lastMetadataBlockFlag;//1 bit used 31 | //byte blockType;//7 bits used 32 | //int length;//24 bits used 33 | 34 | /** 35 | * Enum containing the different Metadata block types. See the FLAC spec 36 | * for more information on the various types. 37 | */ 38 | public enum MetadataBlockType { 39 | /** A meta-block containing stream configuration information */ 40 | STREAMINFO, 41 | /** A meta-block to pad the stream, allowing other meta-data to be 42 | * written in the future without re-writing the entire stream. 43 | */ 44 | PADDING, 45 | /** Application meta-block*/ 46 | APPLICATION, 47 | /** A meta-block which aids in seeking in the stream */ 48 | SEEKTABLE, 49 | /** A meta-block for tags/comments */ 50 | VORBIS_COMMENT, 51 | /** Cuesheet meta-block */ 52 | CUESHEET, 53 | /** A meta-block to store an image, such as cover-art */ 54 | PICTURE 55 | }; 56 | 57 | /** 58 | * Constructor. This class defines no instance variables and only static 59 | * methods. 60 | */ 61 | public MetadataBlockHeader() { 62 | 63 | } 64 | 65 | 66 | /** 67 | * Create a meta-data block header of the given type, and return the result 68 | * in a new EncodedElement(so data is ready to be placed directly in FLAC 69 | * stream) 70 | * 71 | * @param lastBlock True if this is the last meta-block in the stream. False 72 | * otherwise. 73 | * 74 | * @param type enum indicating which type of block we're creating. 75 | * @param length Length of the meta-data block which follows this header. 76 | * @return EncodedElement containing the header. 77 | */ 78 | public static EncodedElement getMetadataBlockHeader(boolean lastBlock, 79 | MetadataBlockType type, int length) { 80 | EncodedElement ele = new EncodedElement(4, 0); 81 | int encodedLastBlock = (lastBlock) ? 1:0; 82 | ele.addInt(encodedLastBlock, 1); 83 | int encodedType = 0; 84 | MetadataBlockType[] vals = MetadataBlockType.values(); 85 | for(int i = 0; i < vals.length; i++) { 86 | if(vals[i] == type) { 87 | encodedType = i; 88 | break; 89 | } 90 | } 91 | 92 | ele.addInt(encodedType, 7); 93 | ele.addInt(length, 24); 94 | return ele; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/MetadataBlockStreamInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | package javaFlacEncoder; 21 | 22 | /** 23 | * MetadataBlockStreamInfo is used to declare the initial stream parameters, 24 | * such as Sample Rate, bits per sample, and number of channels, as well as 25 | * information on the encoded stream such as total number of samples, minimum 26 | * and maximum block and frame sizes, and md5sum of raw audio samples. A 27 | * StreamInfo block must be the first meta-data block in a FLAC stream, and only 28 | * one StreamInfo block may exist. 29 | * 30 | * @author Preston Lacey 31 | */ 32 | public class MetadataBlockStreamInfo { 33 | /** For Debugging: Higher level equals more debug statements */ 34 | static int DEBUG_LEV = 0; 35 | /* int minimumBlockSize = 4096;//32768;//16 bits used; 16 minimum valid 36 | int maximumBlockSize = 4096;//32768;//16 bits used; 65535 maximum valid 37 | int minimumFrameSize = 0;//24 bits used; zero implies unknown 38 | int maximumFrameSize = 0;//24 bits used; zero implies unknown 39 | int sampleRate = 8000;//4096;//20 bits used, but 655350Hz max. 0 is invalid. 40 | byte numberOfChannels = 1;//3 bits used 41 | byte bitsPerSample = 16;//5 bits used. values 4-32 valid 42 | long totalSamplesInStream = 0;//36 bits used. 0 implies unknown. 43 | byte[] md5Hash; 44 | */ 45 | /** 46 | * Constructor. This class defines only static methods and fields. 47 | */ 48 | public MetadataBlockStreamInfo() { 49 | 50 | } 51 | 52 | /** 53 | * Create a FLAC StreamInfo metadata block with the given parameters. Because 54 | * of the data stored in a StreamInfo block, this should generally be created 55 | * only after all encoding is done. 56 | * 57 | * @param sc StreamConfiguration used in this FLAC stream. 58 | * @param minFrameSize Size of smallest frame in FLAC stream. 59 | * @param maxFrameSize Size of largest frame in FLAC stream. 60 | * @param samplesInStream Total number of inter-channel audio samples in 61 | * FLAC stream. 62 | * @param md5Hash MD5 hash of the raw audio samples. 63 | * @return EncodedElement containing created StreamInfo block. 64 | */ 65 | public static EncodedElement getStreamInfo(StreamConfiguration sc, 66 | int minFrameSize, int maxFrameSize, long samplesInStream, byte[] md5Hash) { 67 | int bytes = getByteSize(); 68 | EncodedElement ele = new EncodedElement(bytes, 0); 69 | int encodedBitsPerSample = sc.getBitsPerSample()-1; 70 | ele.addInt(sc.getMinBlockSize(), 16); 71 | ele.addInt(sc.getMaxBlockSize(), 16); 72 | ele.addInt(minFrameSize, 24); 73 | ele.addInt(maxFrameSize, 24); 74 | ele.addInt(sc.getSampleRate(), 20); 75 | ele.addInt(sc.getChannelCount()-1, 3); 76 | ele.addInt(encodedBitsPerSample, 5); 77 | ele.addLong(samplesInStream, 36); 78 | for(int i = 0; i < 16; i++) { 79 | ele.addInt(md5Hash[i], 8); 80 | } 81 | return ele; 82 | } 83 | 84 | /** 85 | * Get the expected size of a properly formed STREAMINFO metadata block. 86 | * 87 | * @return size of properly formed FLAC STREAMINFO metadata block. 88 | */ 89 | static public int getByteSize() { 90 | int size = 0; 91 | size += 16; 92 | size += 16; 93 | size += 24; 94 | size += 24; 95 | size += 20; 96 | size += 3; 97 | size += 5; 98 | size += 36; 99 | size += 64; 100 | size += 64; 101 | size = size/8; 102 | return size; 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/javaFlacEncoder/README: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/ 3 | * All Rights Reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | The javaFlacEncoder project provides a FLAC encoder implemented in java. 21 | It is designed to enable easy addition of FLAC encoding support in java 22 | applications, though a command line file encoding utility is included as well. 23 | For usage of the command line encoder, see the "Console Usage" section below. 24 | 25 | For more information, go to http://javaflacencoder.sourceforge.net. 26 | See javadocs for information on how to make use of this library in your own 27 | application. 28 | 29 | 30 | 31 | Console Usage: 32 |
42 | * If canSeek() returns true: Data will be written as it becomes available, and 43 | * the encoder will seek() to a point near the beginning of the stream to fix 44 | * the stream headers once the stream is closed.
45 | *
10 | If you're simply needing to convert a file(as opposed to a stream), you may
11 | want to use the FLAC_FileEncoder class.
12 |
14 | For applications using the javax.sound API, this library includes basic support.
15 | FLACFileWriter provides the implementation of
16 | javax.sound.sampled.spi.AudioFileWriter. After installing a release jar in the
17 | appropriate location, the FLAC encoder should be available for use by any application
18 | which makes use of the sound api for transcoding purposes. Use this if you need basic
19 | encoding support(with default configuration), and have a
20 | javax.sound.sampled.AudioInputStream as your source.
22 | The remaining example is for those who need more control over the encoding
23 | process, including supplying a non-default EncodingConfiguration object, or
24 | for whom the prior methods are not otherwise suitable. For direct, low-level
25 | access, an application should primarily use the classes FLACEncoder,
26 | EncodingConfiguration, StreamConfiguration, and FLACOutputStream.
27 |
30 | 1) Set StreamConfiguration to appropriate values. After a stream is opened, 31 | this must not be altered until the stream is closed.
32 | 2) Set FLACOutputStream, object to write results to.
33 | 3) Open FLAC Stream
34 | 4) Set EncodingConfiguration(if defaults are insufficient).
35 | 5) Add samples to encoder
36 | 6) Encode Samples
37 | 7) Close stream
38 | (note: steps 4,5, and 6 may be done repeatedly, in any order. However, see 39 | related method documentation for info on concurrent use) 40 |