📄 javasoundcodec.java
字号:
final boolean noRoomInBufferQueue; // copy input buffer to central buffer noRoomInBufferQueue = !bufferQueueInputStream.put(input); // if noRoomInBufferQueue is true, we will return INPUT_BUFFER_NOT_CONSUMED below. // if noRoomInBufferQueue is true, we will have to block and get some actual data. if (audioInputStreamConverted == null) { if (noRoomInBufferQueue) { logger.fine("JavaSoundCodec: audioInputStreamConverted == null, blocking until not null"); // block until input stream is ready try { while (audioInputStreamConverted == null) Thread.sleep(100); } catch (InterruptedException e) { return BUFFER_PROCESSED_FAILED; } } else { logger.fine("JavaSoundCodec: audioInputStreamConverted == null, returning OUTPUT_BUFFER_NOT_FILLED"); output.setLength(0); return OUTPUT_BUFFER_NOT_FILLED; } } // the potential problem with available() is if it returns too little, or 0, when it could return more. This is allowed. // if our buffer queue is full, we'll need to do a potentially blocking read, even if available() returns 0. // if available violates the spec and returns more than it should, we have to use another threa. final int avail = audioInputStreamConverted.available(); if (trace) logger.fine("audioInputStreamConverted.available() == " + avail + ", bufferQueueInputStream.available() = " + bufferQueueInputStream.available()); if (output.getData() == null) output.setData(new byte[10000]); // TODO: size. output.setFormat(getOutputFormat()); final byte[] data = (byte []) output.getData(); // if audioInputStreamConverted has reached EOM, available will return 0. // we assume that audioInputStreamConverted can only reach EOM after the input has // reached EOM, so if the input has reached EOM, we ignore avail, and go ahead and // either do a blocking read or read to get -1. int lenToRead; if (noRoomInBufferQueue || input.isEOM()) lenToRead = data.length; // force a potentially blocking read else lenToRead = avail > data.length ? data.length : avail; if (lenToRead == 0) { logger.finer("JavaSoundCodec: lenToRead == 0, returning OUTPUT_BUFFER_NOT_FILLED. input.isEOM()=" + input.isEOM()); // TODO: detect EOM output.setLength(0); return OUTPUT_BUFFER_NOT_FILLED; } // TODO: set format // TODO: Tritonus (ulaw conversion) has a flawed input stream implementation. audioInputStreamConverted.available() is supposed to // return the number of bytes that can be read without blocking. But when we call with this number, // tritonus goes ahead and reads from the BufferQueueInputStream. This causes a lockup. // final int lenRead = audioInputStreamConverted.read(data, 0, lenToRead); logger.finer("JavaSoundCodec: Read from audioInputStreamConverted: " + lenRead); if (lenRead == -1) { logger.fine("total in: " + totalIn + " total out: " + totalOut); output.setEOM(true); output.setLength(0); return BUFFER_PROCESSED_OK; } output.setLength(lenRead); totalOut += lenRead; return (noRoomInBufferQueue || input.isEOM()) ? INPUT_BUFFER_NOT_CONSUMED : BUFFER_PROCESSED_OK; // if input is EOM, then they have to keep calling us with it until we finish processing } catch (IOException e) { output.setLength(0); return BUFFER_PROCESSED_FAILED; } } /** * See the comments in JavaSoundParser for more info. * AudioSystem.getAudioInputStream lets us specify the output format, but it does not let us specify the input format. * It gets the input format by reading the stream. However, the parser has already stripped off the headers, and the data * we are getting is pure. To use JavaSound, we have to create a fake header based on the input format information. * It does not matter whether this header matches the original header, it just has to get AudioSystem.getAudioInputStream * To figure out what kind of format it is. * */ private static byte[] fakeHeader(javax.sound.sampled.AudioFormat f) { // headers are not stripped off for MPEG 3 // TODO: check using class name, to avoid class not found exception if javazoom is not in the classpath. if (f instanceof javazoom.spi.mpeg.sampled.file.MpegAudioFormat) return new byte[0]; // empty header if (f instanceof VorbisAudioFormat) return new byte[0]; // empty header byte [] result = createAuHeader(f); if (result != null) return result; result = createWavHeader(f); if (result != null) return result; return null; // not able to create a header } /** * Used to create a "fake" AU header for fakeHeader. * See http://en.wikipedia.org/wiki/Au_file_format. * The Au file format is a simple audio file format that consists of a header of 6 32-bit words and then the data (high-order byte comes first). The format was introduced by Sun Microsystems. */ private static byte[] createAuHeader(javax.sound.sampled.AudioFormat f) { byte[] result = new byte[4 * 6]; encodeIntBE(0x2e736e64, result, 0); // the value 0x2e736e64 (four ASCII characters ".snd") encodeIntBE(result.length, result, 4); // the offset to the data in bytes. The minimum valid number is 24 (decimal). encodeIntBE(0xffffffff, result, 8); // data size in bytes. If unknown, the value 0xffffffff should be used. //Data encoding format: // 1=8-bit ISDN u-law, 2=8-bit linear PCM [REF-PCM], 3=16-bit linear PCM, 4=24-bit linear PCM, // 5=32-bit linear PCM, 6=32-bit IEEE floating point, 7=64-bit IEEE floating point, // 23=8-bit ISDN u-law compressed using the UIT-T G.721 ADPCM voice data encoding scheme. final int encoding; if (f.getEncoding() == Encoding.ALAW) { if (f.getSampleSizeInBits() == 8) encoding = 27; else return null; } else if (f.getEncoding() == Encoding.ULAW) { if (f.getSampleSizeInBits() == 8) encoding = 1; else return null; } else if (f.getEncoding() == Encoding.PCM_SIGNED) { // AU appears to be signed when it uses PCM if (f.getSampleSizeInBits() == 8) encoding = 2; else if (f.getSampleSizeInBits() == 16) encoding = 3; else if (f.getSampleSizeInBits() == 24) encoding = 4; else if (f.getSampleSizeInBits() == 32) encoding = 5; else return null; } else if (f.getEncoding() == Encoding.PCM_UNSIGNED) {// AU appears to be signed when it uses PCM return null; } else { return null; } encodeIntBE(encoding, result, 12); // sample rate - the number of samples/second (e.g., 8000) if (f.getSampleRate() < 0) return null; encodeIntBE((int) f.getSampleRate(), result, 16); // channels the number of interleaved channels (e.g., 1 for mono, 2 for stereo) if (f.getChannels() < 0) return null; encodeIntBE(f.getChannels(), result, 20); return result; } /** * See http://ccrma.stanford.edu/courses/422/projects/WaveFormat/. * @param f * @return the header for the wav */ public static byte[] createWavHeader(javax.sound.sampled.AudioFormat f) { // from the web link: // 8-bit samples are stored as unsigned bytes, ranging from 0 to 255. // 16-bit samples are stored as 2's-complement signed integers, ranging from -32768 to 32767. if (f.getEncoding() != Encoding.PCM_SIGNED && f.getEncoding() != Encoding.PCM_UNSIGNED) return null; if (f.getSampleSizeInBits() == 8 && f.getEncoding() != Encoding.PCM_UNSIGNED) return null; if (f.getSampleSizeInBits() == 16 && f.getEncoding() != Encoding.PCM_SIGNED) return null; byte[] result = new byte[44]; // from the web link: // The default byte ordering assumed for WAVE data files is little-endian. // Files written using the big-endian byte ordering scheme have the identifier RIFX instead of RIFF. if (f.getSampleSizeInBits() > 8 && f.isBigEndian()) encodeIntBE(0x52494658, result, 0); // Contains the letters "RIFX" in ASCII form (0x52494658 big-endian form). else encodeIntBE(0x52494646, result, 0); // Contains the letters "RIFF" in ASCII form (0x52494646 big-endian form). int len = Integer.MAX_VALUE; // TODO: it is unknown, what to do? Hopefully JavaSound won't care encodeIntLE(len + result.length - 8, result, 4); // total length minus the first 2 ints encodeIntBE(0x57415645, result, 8); // Contains the letters "WAVE" (0x57415645 big-endian form). // The "WAVE" format consists of two subchunks: "fmt " and "data":// The "fmt " subchunk describes the sound data's format: encodeIntBE(0x666d7420, result, 12); //Contains the letters "fmt " (0x666d7420 big-endian form). encodeIntLE(16, result, 16); // 16 for PCM. This is the size of the rest of the Subchunk which follows this number. encodeShortLE((short) 1, result, 20); // PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression. encodeShortLE((short) f.getChannels(), result, 22); // NumChannels Mono = 1, Stereo = 2, etc. encodeIntLE((int) f.getSampleRate(), result, 24); // SampleRate 8000, 44100, etc. encodeIntLE((((int) f.getSampleRate()) * f.getChannels() * f.getSampleSizeInBits()) / 8, result, 28); //ByteRate == SampleRate * NumChannels * BitsPerSample/8 encodeShortLE((short) ((f.getChannels() * f.getSampleSizeInBits()) / 8), result, 32); //BlockAlign == NumChannels * BitsPerSample/8 The number of bytes for one sample including all channels. I wonder what happens when this number isn't an integer? encodeShortLE((short) f.getSampleSizeInBits(), result, 34); //BitsPerSample 8 bits = 8, 16 bits = 16, etc. // The "data" subchunk contains the size of the data and the actual sound: encodeIntBE(0x64617461, result, 36);// Subchunk2ID Contains the letters "data" (0x64617461 big-endian form). encodeIntLE(len, result, 40); // Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 return result; } private static final int SIZEOF_INT = 4; private static final int SIZEOF_LONG = 8; private static final int SIZEOF_SHORT = 2; private static final int BITS_PER_BYTE = 8; private static final int MAX_SIGNED_BYTE = 127; private static final int MAX_BYTE = 0xFF; private static final int MAX_BYTE_PLUS1 = 256; private static void encodeIntBE(int value, byte[] ba, int offset) { int length = SIZEOF_INT; for (int i = 0; i < length; ++i) { int byteValue = value & MAX_BYTE; if (byteValue > MAX_SIGNED_BYTE) byteValue = byteValue - MAX_BYTE_PLUS1; ba[offset + (length - i - 1)] = (byte) byteValue; value = value >> BITS_PER_BYTE; } } private static void encodeIntLE(int value, byte[] ba, int offset) { int length = SIZEOF_INT; for (int i = 0; i < length; ++i) { int byteValue = value & MAX_BYTE; if (byteValue > MAX_SIGNED_BYTE) byteValue = byteValue - MAX_BYTE_PLUS1; ba[offset + i] = (byte) byteValue; value = value >> BITS_PER_BYTE; } } public static void encodeShortLE(short value, byte[] ba, int offset) { int length = SIZEOF_SHORT; for (int i = 0; i < length; ++i) { int byteValue = value & MAX_BYTE; if (byteValue > MAX_SIGNED_BYTE) byteValue = byteValue - MAX_BYTE_PLUS1; ba[offset + i] = (byte) byteValue; value = (short) (value >> BITS_PER_BYTE); } } public static void encodeShortBE(short value, byte[] ba, int offset) { int length = SIZEOF_SHORT; for (int i = 0; i < length; ++i) { int byteValue = value & MAX_BYTE; if (byteValue > MAX_SIGNED_BYTE) byteValue = byteValue - MAX_BYTE_PLUS1; ba[offset + (length - i - 1)] = (byte) byteValue; value = (short) (value >> BITS_PER_BYTE); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -