/************************************************************************************************************ * WavPlay AM824 * Copyright (C) 2020, Dolby Laboratories Inc. * 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 3 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, see ************************************************************************************************************/ #include #define CHANNEL_STATUS_BYTES 24 #define WIDTH (8) #define BOTTOMBIT 1 #define REFLECTED_POLYNOMIAL 0xb8 // Unreflected is 0x1d enum AM824ErrorCode { AM824_ERR_OK = 0, AM824_ERR_BAD_SAMPLING_FREQUENCY = -1, AM824_ERR_UNSUPPORTED_BITDEPTH = -2 }; enum AM824SamplingFrequency { FS_NOT_INDICATED = 0, FS_44100_HZ = 1, FS_48000_HZ = 2, FS_32000_HZ = 3 }; enum AM824Endianess { AM824_BIG_ENDIAN, AM824_LITTLE_ENDIAN }; class AM824Framer { uint8_t channelStatusIndex; uint8_t channelStatusMask; uint8_t channelStatus[CHANNEL_STATUS_BYTES]; uint8_t subFrameCounter; uint8_t numChannels; uint8_t bitDepth; uint8_t crcTable[256]; AM824Endianess endian; static uint8_t getParity(unsigned int n) { uint8_t parity = 0; while (n) { parity = 1 - parity; n = n & (n - 1); } return parity; } void crcTableInit(void) { uint8_t remainder; for (int dividend = 0; dividend < 256; ++dividend) { remainder = dividend << (WIDTH - 8); for (uint8_t bit = 0; bit < 8; bit++) { if (remainder & BOTTOMBIT) { remainder = (remainder >> 1) ^ REFLECTED_POLYNOMIAL; } else { remainder = (remainder >> 1); } } crcTable[dividend] = remainder; } } void setCRC(void) { uint8_t data; uint8_t remainder = 0xff; for (int byte = 0; byte < 23; byte++) { data = channelStatus[byte] ^ remainder; remainder = crcTable[data]; } channelStatus[23] = remainder; } public: // Input number of channels and the bitdepth of the input samples // Note that the output bit depth is always 24 bit AM824Framer( uint8_t newNumChannels, /* Input - Number of channels of input/output audio */ uint8_t newBitDepth, /* Input - Bit depth of input audio, output is always 32 bit */ AM824Endianess outputEndianess, /* Input = Endianess of output samples, input is always machine order */ AM824ErrorCode& err) /* Output - error code */ { uint8_t i; channelStatusIndex = 0; channelStatusMask = 1; numChannels = newNumChannels; subFrameCounter = 0; bitDepth = newBitDepth; endian = outputEndianess; // Set certain channel status bits // Clear it first for (i = 0; i < CHANNEL_STATUS_BYTES; i++) { channelStatus[i] = 0; } // Default Channel Status // Professional Bit channelStatus[0] |= 1; // Non-audio PCM channelStatus[0] |= 1 << 1; // 48kHz channelStatus[0] |= 2 << 6; switch (bitDepth) { case 16: channelStatus[2] |= 1 << 3; break; case 20: // Use of Auxillary bits channelStatus[2] |= 4; // 20 bit data channelStatus[2] |= 1 << 3; break; case 24: // Use of Auxillary bits channelStatus[2] |= 4; // 24 bit data channelStatus[2] |= 5 << 3; break; default: err = AM824_ERR_UNSUPPORTED_BITDEPTH; return; } crcTableInit(); setCRC(); err = AM824_ERR_OK; } AM824ErrorCode setSamplingFrequency(AM824SamplingFrequency fs_code) { if (fs_code > FS_32000_HZ) { return (AM824_ERR_BAD_SAMPLING_FREQUENCY); } // Reset top two bits and set accordingly channelStatus[0] &= 0x3f; channelStatus[0] |= fs_code << 6; setCRC(); return (AM824_ERR_OK); } void setProfessionalMode(void) { channelStatus[0] |= 1; setCRC(); } void setConsumerMode(void) { channelStatus[0] &= 0xfe; setCRC(); } void setAudioMode(void) { channelStatus[0] &= 0xfd; setCRC(); } void setDataMode(void) { channelStatus[0] |= 2; setCRC(); } void getAM824Sample(uint32_t inputSample, uint8_t* outputBytes) { uint32_t outputSample; bool channelStatusBit = channelStatus[channelStatusIndex] & channelStatusMask; bool userBit = false; bool validityBit = false; // Input samples are MSB justified as per AES3 if (bitDepth == 16) { outputSample = inputSample << 8; } else { outputSample = inputSample; } // Detect block start if ((channelStatusIndex == 0) && (channelStatusMask == 1)) { outputSample |= 1 << 29; } // Detect frame start if (subFrameCounter == 0) { outputSample |= 1 << 28; } outputSample |= channelStatusBit << 26; outputSample |= userBit << 25; outputSample |= validityBit << 24; // Now complete except for parity so calculate that outputSample |= getParity(outputSample) << 27; // Now complete all the wraparound checks // Note that channel status chan be different for the different subframes // (channels) but in this example channel status is set to be the same for // all subframes (channels) subFrameCounter++; if (subFrameCounter == numChannels) { subFrameCounter = 0; // Move to next channel status bit if (channelStatusMask == 0x80) { channelStatusMask = 1; channelStatusIndex++; if (channelStatusIndex == CHANNEL_STATUS_BYTES) { channelStatusIndex = 0; } } else { channelStatusMask = channelStatusMask << 1; } } if (endian == AM824_BIG_ENDIAN) { // Return in 32 bit Big Endian format *outputBytes++ = (outputSample & 0xff000000) >> 24; *outputBytes++ = (outputSample & 0x00ff0000) >> 16; *outputBytes++ = (outputSample & 0x0000ff00) >> 8; *outputBytes++ = (outputSample & 0x000000ff) >> 0; } else { // Return in 32 bit Little Endian format *outputBytes++ = (outputSample & 0x000000ff) >> 0; *outputBytes++ = (outputSample & 0x0000ff00) >> 8; *outputBytes++ = (outputSample & 0x00ff0000) >> 16; *outputBytes++ = (outputSample & 0xff000000) >> 24; } } /* Simple test code to check CRC implementation */ /* See EBU Tech 3250 or AES3 for the reference for these examples */ void testCRC(void) { unsigned int i; for (i = 0; i < CHANNEL_STATUS_BYTES; i++) { channelStatus[i] = 0; } // From AES3-2-2009-r2019 - Example 1 channelStatus[0] = 0x3d; channelStatus[1] = 2; channelStatus[4] = 2; setCRC(); if (channelStatus[23] == 0x9b) { printf("Example 1 - passed\n"); } else { printf("Example 1 - failed, expecting 0x9b, got 0x%x\n", channelStatus[23]); } channelStatus[0] = 0x01; channelStatus[1] = 0; channelStatus[4] = 0; setCRC(); if (channelStatus[23] == 0x32) { printf("Example 2 - passed\n"); } else { printf("Example 2 - failed, expecting 0x32, got 0x%x\n", channelStatus[23]); } } };