diff --git a/wavplay_am824/Makefile.Linux b/wavplay_am824/Makefile.Linux new file mode 100644 index 0000000..d0edf09 --- /dev/null +++ b/wavplay_am824/Makefile.Linux @@ -0,0 +1,35 @@ +CFLAGS += -g + +CC = g++ + +ARCH = Linux + +SRC = wavplay_am824.cpp + +HEADERS = am824_framer.h + +EXE = bin/$(ARCH)/wavplay_am824 + +OBJ = bin/$(ARCH)/wavplay_am824.o + +LIBS = -lportaudio -lrt -lm -lsndfile -lasound -lpthread + +BIN = bin/$(ARCH) + +LD = g++ + +LDFLAGS = -L/usr/local/lib -L/usr/lib/x86_64-linux-gnu -pthread -g + +all: $(BIN) $(EXE) + +$(BIN) : + mkdir -p $(BIN) + +$(OBJ) : $(SRC) $(HEADERS) + $(CC) -c $< -o $@ $(CFLAGS) + +$(EXE): $(OBJ) $(PALIB) + $(LD) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +clean: + rm -f bin/$(ARCH)/* diff --git a/wavplay_am824/am824_framer.h b/wavplay_am824/am824_framer.h new file mode 100644 index 0000000..ccfd527 --- /dev/null +++ b/wavplay_am824/am824_framer.h @@ -0,0 +1,297 @@ + +/************************************************************************************************************ + * 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]); + } + } + +}; \ No newline at end of file