diff options
Diffstat (limited to 'audio_utils/spdif/SPDIFEncoder.cpp')
-rw-r--r-- | audio_utils/spdif/SPDIFEncoder.cpp | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/audio_utils/spdif/SPDIFEncoder.cpp b/audio_utils/spdif/SPDIFEncoder.cpp new file mode 100644 index 00000000..07a1800d --- /dev/null +++ b/audio_utils/spdif/SPDIFEncoder.cpp @@ -0,0 +1,229 @@ +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#include <stdint.h> + +#define LOG_TAG "AudioHardwareTungsten" +#include <utils/Log.h> +#include <audio_utils/spdif/SPDIFEncoder.h> + +namespace android { + +// Burst Preamble defined in IEC61937-1 +const unsigned short SPDIFEncoder::kSPDIFSync1 = 0xF872; // Pa +const unsigned short SPDIFEncoder::kSPDIFSync2 = 0x4E1F; // Pb + +static int32_t sEndianDetector = 1; +#define isLittleEndian() (*((uint8_t *)&sEndianDetector)) + +SPDIFEncoder::SPDIFEncoder() + : mState(STATE_IDLE) + , mSampleRate(48000) + , mBurstBuffer(NULL) + , mBurstBufferSizeBytes(0) + , mRateMultiplier(1) + , mBurstFrames(0) + , mByteCursor(0) + , mBitstreamNumber(0) + , mPayloadBytesPending(0) +{ + // TODO support other compressed audio formats + mFramer = new AC3FrameScanner(); + + mBurstBufferSizeBytes = sizeof(uint16_t) + * SPDIF_ENCODED_CHANNEL_COUNT + * mFramer->getMaxSampleFramesPerSyncFrame(); + ALOGI("SPDIFEncoder: mBurstBufferSizeBytes = %d, littleEndian = %d", + mBurstBufferSizeBytes, isLittleEndian()); + mBurstBuffer = new uint16_t[mBurstBufferSizeBytes >> 1]; + clearBurstBuffer(); +} + +SPDIFEncoder::~SPDIFEncoder() +{ + delete[] mBurstBuffer; +} + +int SPDIFEncoder::getBytesPerOutputFrame() +{ + return SPDIF_ENCODED_CHANNEL_COUNT * sizeof(int16_t); +} + +void SPDIFEncoder::writeBurstBufferShorts(const uint16_t *buffer, size_t numShorts) +{ + mByteCursor = (mByteCursor + 1) & ~1; // round up to even byte + size_t bytesToWrite = numShorts * sizeof(uint16_t); + if ((mByteCursor + bytesToWrite) > mBurstBufferSizeBytes) { + ALOGE("SPDIFEncoder: Burst buffer overflow!\n"); + clearBurstBuffer(); + return; + } + memcpy(&mBurstBuffer[mByteCursor >> 1], buffer, bytesToWrite); + mByteCursor += bytesToWrite; +} + +// Pack the bytes into the short buffer in the order: +// byte[0] -> short[0] MSB +// byte[1] -> short[0] LSB +// byte[2] -> short[1] MSB +// byte[3] -> short[1] LSB +// etcetera +// This way they should come out in the correct order for SPDIF on both +// Big and Little Endian CPUs. +void SPDIFEncoder::writeBurstBufferBytes(const uint8_t *buffer, size_t numBytes) +{ + size_t bytesToWrite = numBytes; + if ((mByteCursor + bytesToWrite) > mBurstBufferSizeBytes) { + ALOGE("SPDIFEncoder: Burst buffer overflow!\n"); + clearBurstBuffer(); + return; + } + uint16_t pad = mBurstBuffer[mByteCursor >> 1]; + for (size_t i = 0; i < bytesToWrite; i++) { + if (mByteCursor & 1 ) { + pad |= *buffer++; // put second byte in LSB + mBurstBuffer[mByteCursor >> 1] = pad; + pad = 0; + } else { + pad |= (*buffer++) << 8; // put first byte in MSB + } + mByteCursor++; + } + // Save partially filled short. + if (mByteCursor & 1 ){ + mBurstBuffer[mByteCursor >> 1] = pad; + } +} + +void SPDIFEncoder::sendZeroPad() +{ + // Pad remainder of burst with zeros. + size_t burstSize = mFramer->getSampleFramesPerSyncFrame() * sizeof(uint16_t) + * SPDIF_ENCODED_CHANNEL_COUNT; + if (mByteCursor > burstSize) { + ALOGE("SPDIFEncoder: Burst buffer, contents too large!\n"); + clearBurstBuffer(); + } else { + // We don't have to write zeros because buffer already set to zero + // by clearBurstBuffer(). + mByteCursor = burstSize; + } +} + +void SPDIFEncoder::flushBurstBuffer() +{ + if (mByteCursor > 0) { + sendZeroPad(); + writeOutput(mBurstBuffer, mByteCursor); + clearBurstBuffer(); + } +} + +void SPDIFEncoder::clearBurstBuffer() +{ + if (mBurstBuffer) { + memset(mBurstBuffer, 0, mBurstBufferSizeBytes); + } + mByteCursor = 0; +} + +size_t SPDIFEncoder::startDataBurst() +{ + // Encode IEC61937-1 Burst Preamble + uint16_t preamble[4]; + + uint16_t burstInfo = (mBitstreamNumber << 13) + | (mFramer->getDataTypeInfo() << 8) + | mFramer->getDataType(); + + mRateMultiplier = mFramer->getRateMultiplier(); + + preamble[0] = kSPDIFSync1; + preamble[1] = kSPDIFSync2; + preamble[2] = burstInfo; + preamble[3] = mFramer->getLengthCode(); + writeBurstBufferShorts(preamble, 4); + + // Write start of encoded frame that was buffered in frame detector. + size_t syncSize = mFramer->getHeaderSizeBytes(); + writeBurstBufferBytes(mFramer->getHeaderAddress(), syncSize); + ALOGV("SPDIFEncoder: startDataBurst, syncSize = %u, lengthCode = %d", + syncSize, mFramer->getLengthCode()); + return mFramer->getFrameSizeBytes() - syncSize; +} + +// Wraps raw encoded data into a data burst. +ssize_t SPDIFEncoder::write( const void *buffer, size_t numBytes ) +{ + size_t bytesLeft = numBytes; + const uint8_t *data = (const uint8_t *)buffer; + ALOGV("SPDIFEncoder: state = %d, write(buffer[0] = 0x%02X, numBytes = %u)", + mState, (uint) *data, numBytes); + while (bytesLeft > 0) { + State nextState = mState; + switch (mState) { + // Look for beginning of encoded frame. + case STATE_IDLE: + if (mFramer->scan(*data)) { + if (mFramer->isFirstInBurst()) { + // Make sure that this frame is at the beginning of the data burst. + flushBurstBuffer(); + } + mPayloadBytesPending = startDataBurst(); + nextState = STATE_BURST; + } + data++; + bytesLeft--; + break; + + // Write payload until we hit end of frame. + case STATE_BURST: + { + size_t bytesToWrite = bytesLeft; + // Only write as many as we need to finish the frame. + if (bytesToWrite > mPayloadBytesPending) { + bytesToWrite = mPayloadBytesPending; + } + writeBurstBufferBytes(data, bytesToWrite); + + data += bytesToWrite; + bytesLeft -= bytesToWrite; + mPayloadBytesPending -= bytesToWrite; + + // If we have all the payload then send a data burst. + if (mPayloadBytesPending == 0) { + if (mFramer->isLastInBurst()) { + // Flush now if we know the burst is ready. + flushBurstBuffer(); + } + nextState = STATE_IDLE; + } + } + break; + + default: + ALOGE("SPDIFEncoder: illegal state = %d\n", mState); + nextState = STATE_IDLE; + break; + } + mState = nextState; + } + return numBytes; +} + +} // namespace android |