feat: add G.711 a-law algorithm (#858)

* feat: add G.711 a-law algorithm

* chore: add CMakeLists.txt for audio/

* updating DIRECTORY.md

* docs: add explanation to G.711 a-law algorithm

* docs: adjust comments to G.711 a-law algorithm

Co-authored-by: David Leal <halfpacho@gmail.com>

* docs: adjust comments to G.711 a-law algorithm

Co-authored-by: David Leal <halfpacho@gmail.com>

* test: add self-test for G.711 a-law algorithm

* fix: initialize variables to zero

* docs: adjust comments to G.711 a-law algorithm

Co-authored-by: David Leal <halfpacho@gmail.com>

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: David Leal <halfpacho@gmail.com>
This commit is contained in:
Alliswell 2021-10-04 02:16:43 +08:00 committed by GitHub
parent 690d49099d
commit 97d021424e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 236 additions and 2 deletions

View File

@ -18,7 +18,7 @@ endif(MSVC)
# addresses a bug when linking on OSX
find_library(MATH_LIBRARY m)
# Optional flag - can be set by user
# Optional flag - can be set by user
# Default "ON"
option(USE_OPENMP "flag to use OpenMP for multithreading" ON)
if(USE_OPENMP)
@ -46,12 +46,13 @@ if (NOT HAS_INTTYPES_H)
message(FATAL_ERROR "Missing required header: 'inttypes.h'")
endif()
## Add subdirectories containing CMakeLists.txt
## Add subdirectories containing CMakeLists.txt
# to configure and compile files in the respective folders
add_subdirectory(developer_tools)
add_subdirectory(hash)
add_subdirectory(misc)
add_subdirectory(games)
add_subdirectory(audio)
add_subdirectory(sorting)
add_subdirectory(geometry)
add_subdirectory(graphics)

View File

@ -1,5 +1,8 @@
# List of all files
## Audio
* [Alaw](https://github.com/TheAlgorithms/C/blob/master/audio/alaw.c)
## Client Server
* [Client](https://github.com/TheAlgorithms/C/blob/master/client_server/client.c)
* [Server](https://github.com/TheAlgorithms/C/blob/master/client_server/server.c)

14
audio/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c )
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".c" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
install(TARGETS ${testname} DESTINATION "bin/audio")
endforeach( testsourcefile ${APP_SOURCES} )

216
audio/alaw.c Normal file
View File

@ -0,0 +1,216 @@
/**
* @file
* @author [sunzhenliang](https://github.com/HiSunzhenliang)
* @brief A-law algorithm for encoding and decoding (16bit pcm <=> a-law).
* This is the implementation of [G.711](https://en.wikipedia.org/wiki/G.711)
* in C.
**/
/**
* Linear input code | Compressed code | Linear output code
* ------------------+-----------------+-------------------
* s0000000abcdx | s000abcd | s0000000abcd1
* s0000001abcdx | s001abcd | s0000001abcd1
* s000001abcdxx | s010abcd | s000001abcd10
* s00001abcdxxx | s011abcd | s00001abcd100
* s0001abcdxxxx | s100abcd | s0001abcd1000
* s001abcdxxxxx | s101abcd | s001abcd10000
* s01abcdxxxxxx | s110abcd | s01abcd100000
* s1abcdxxxxxxx | s111abcd | s1abcd1000000
*
* Compressed code: (s | eee | abcd)
**/
#include <assert.h> /// for assert
#include <inttypes.h> /// for appropriate size int types
#include <stdio.h> /// for IO operations
/* length of test inputs */
#define LEN ((size_t)8)
/* input pcm for test */
int16_t pcm[LEN] = {1000, -1000, 1234, 3200, -1314, 0, 32767, -32768};
/* result coded alaw for test */
uint8_t r_coded[LEN] = {250, 122, 230, 156, 97, 213, 170, 42};
/* result decoded for test */
int16_t r_decoded[LEN] = {1008, -1008, 1248, 3264, -1312, 8, 32256, -32256};
/**
* @brief 16bit pcm to 8bit alaw
* @param out unsigned 8bit alaw array
* @param in signed 16bit pcm array
* @param len length of pcm array
* @returns void
*/
void encode(uint8_t *out, int16_t *in, size_t len)
{
uint8_t alaw = 0;
int16_t pcm = 0;
int32_t sign = 0;
int32_t abcd = 0;
int32_t eee = 0;
int32_t mask = 0;
for (size_t i = 0; i < len; i++)
{
pcm = *in++;
/* 0-7 kinds of quantization level from the table above */
eee = 7;
mask = 0x4000; /* 0x4000: '0b0100 0000 0000 0000' */
/* Get sign bit */
sign = (pcm & 0x8000) >> 8;
/* Turn negative pcm to positive */
/* The absolute value of a negative number may be larger than the size
* of the corresponding positive number, so here needs `-pcm -1` after
* taking the opposite number. */
pcm = sign ? (-pcm - 1) : pcm;
/* Get eee and abcd bit */
/* Use mask to locate the first `1` bit and quantization level at the
* same time */
while ((pcm & mask) == 0 && eee > 0)
{
eee--;
mask >>= 1;
}
/* The location of abcd bits is related with quantization level. Check
* the table above to determine how many bits to `>>` to get abcd */
abcd = (pcm >> (eee ? (eee + 3) : 4)) & 0x0f;
/* Put the quantization level number at right bit location to get eee
* bits */
eee <<= 4;
/* Splice results */
alaw = (sign | eee | abcd);
/* The standard specifies that all resulting even bits (LSB
* is even) are inverted before the octet is transmitted. This is to
* provide plenty of 0/1 transitions to facilitate the clock recovery
* process in the PCM receivers. Thus, a silent A-law encoded PCM
* channel has the 8 bit samples coded 0xD5 instead of 0x80 in the
* octets. (Reference from wiki above) */
*out++ = alaw ^ 0xD5;
}
}
/**
* @brief 8bit alaw to 16bit pcm
* @param out signed 16bit pcm array
* @param in unsigned 8bit alaw array
* @param len length of alaw array
* @returns void
*/
void decode(int16_t *out, uint8_t *in, size_t len)
{
uint8_t alaw = 0;
int32_t pcm = 0;
int32_t sign = 0;
int32_t eee = 0;
for (size_t i = 0; i < len; i++)
{
alaw = *in++;
/* Re-toggle toggled bits */
alaw ^= 0xD5;
/* Get sign bit */
sign = alaw & 0x80;
/* Get eee bits */
eee = (alaw & 0x70) >> 4;
/* Get abcd bits and add 1/2 quantization step */
pcm = (alaw & 0x0f) << 4 | 8;
/* If quantization level > 0, there need `1` bit before abcd bits */
pcm += eee ? 0x100 : 0x0;
/* Left shift according quantization level */
pcm <<= eee > 1 ? (eee - 1) : 0;
/* Use the right sign */
*out++ = sign ? -pcm : pcm;
}
}
/**
* @brief Self-test implementations
* @param pcm signed 16bit pcm array
* @param coded unsigned 8bit alaw array
* @param decoded signed 16bit pcm array
* @param len length of test array
* @returns void
*/
static void test(int16_t *pcm, uint8_t *coded, int16_t *decoded, size_t len)
{
/* run encode */
encode(coded, pcm, len);
/* check encode result */
for (size_t i = 0; i < len; i++)
{
assert(coded[i] == r_coded[i]);
}
/* run decode */
decode(decoded, coded, len);
/* check decode result */
for (size_t i = 0; i < len; i++)
{
assert(decoded[i] == r_decoded[i]);
}
}
/**
* @brief Main function
* @param argc commandline argument count (ignored)
* @param argv commandline array of arguments (ignored)
* @returns 0 on exit
*/
int main(int argc, char *argv[])
{
/* output alaw encoded by encode() */
uint8_t coded[LEN];
/* output pcm decoded by decode() from coded[LEN] */
int16_t decoded[LEN];
test(pcm, coded, decoded, LEN); // run self-test implementations
/* print test pcm inputs */
printf("inputs: ");
for (size_t i = 0; i < LEN; i++)
{
printf("%d ", pcm[i]);
}
printf("\n");
/* print encoded alaw */
printf("encode: ");
for (size_t i = 0; i < LEN; i++)
{
printf("%u ", coded[i]);
}
printf("\n");
/* print decoded pcm */
printf("decode: ");
for (size_t i = 0; i < LEN; i++)
{
printf("%d ", decoded[i]);
}
printf("\n");
/* It can be seen that the encoded alaw is smaller than the input PCM, so
* the purpose of compression is achieved. And the decoded PCM is almost the
* same as the original input PCM, which verifies the correctness of the
* decoding. The reason why it is not exactly the same is that there is
* precision loss during encode / decode. */
return 0;
}