Skip to content

Commit 97d0214

Browse files
HiSunzhenlianggithub-actionsPanquesito7
authored
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 <[email protected]> * docs: adjust comments to G.711 a-law algorithm Co-authored-by: David Leal <[email protected]> * 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 <[email protected]> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal <[email protected]>
1 parent 690d490 commit 97d0214

File tree

4 files changed

+236
-2
lines changed

4 files changed

+236
-2
lines changed

CMakeLists.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ endif(MSVC)
1818
# addresses a bug when linking on OSX
1919
find_library(MATH_LIBRARY m)
2020

21-
# Optional flag - can be set by user
21+
# Optional flag - can be set by user
2222
# Default "ON"
2323
option(USE_OPENMP "flag to use OpenMP for multithreading" ON)
2424
if(USE_OPENMP)
@@ -46,12 +46,13 @@ if (NOT HAS_INTTYPES_H)
4646
message(FATAL_ERROR "Missing required header: 'inttypes.h'")
4747
endif()
4848

49-
## Add subdirectories containing CMakeLists.txt
49+
## Add subdirectories containing CMakeLists.txt
5050
# to configure and compile files in the respective folders
5151
add_subdirectory(developer_tools)
5252
add_subdirectory(hash)
5353
add_subdirectory(misc)
5454
add_subdirectory(games)
55+
add_subdirectory(audio)
5556
add_subdirectory(sorting)
5657
add_subdirectory(geometry)
5758
add_subdirectory(graphics)

DIRECTORY.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# List of all files
22

3+
## Audio
4+
* [Alaw](https://github.com/TheAlgorithms/C/blob/master/audio/alaw.c)
5+
36
## Client Server
47
* [Client](https://github.com/TheAlgorithms/C/blob/master/client_server/client.c)
58
* [Server](https://github.com/TheAlgorithms/C/blob/master/client_server/server.c)

audio/CMakeLists.txt

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

audio/alaw.c

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/**
2+
* @file
3+
* @author [sunzhenliang](https://github.com/HiSunzhenliang)
4+
* @brief A-law algorithm for encoding and decoding (16bit pcm <=> a-law).
5+
* This is the implementation of [G.711](https://en.wikipedia.org/wiki/G.711)
6+
* in C.
7+
**/
8+
9+
/**
10+
* Linear input code | Compressed code | Linear output code
11+
* ------------------+-----------------+-------------------
12+
* s0000000abcdx | s000abcd | s0000000abcd1
13+
* s0000001abcdx | s001abcd | s0000001abcd1
14+
* s000001abcdxx | s010abcd | s000001abcd10
15+
* s00001abcdxxx | s011abcd | s00001abcd100
16+
* s0001abcdxxxx | s100abcd | s0001abcd1000
17+
* s001abcdxxxxx | s101abcd | s001abcd10000
18+
* s01abcdxxxxxx | s110abcd | s01abcd100000
19+
* s1abcdxxxxxxx | s111abcd | s1abcd1000000
20+
*
21+
* Compressed code: (s | eee | abcd)
22+
**/
23+
#include <assert.h> /// for assert
24+
#include <inttypes.h> /// for appropriate size int types
25+
#include <stdio.h> /// for IO operations
26+
27+
/* length of test inputs */
28+
#define LEN ((size_t)8)
29+
30+
/* input pcm for test */
31+
int16_t pcm[LEN] = {1000, -1000, 1234, 3200, -1314, 0, 32767, -32768};
32+
33+
/* result coded alaw for test */
34+
uint8_t r_coded[LEN] = {250, 122, 230, 156, 97, 213, 170, 42};
35+
36+
/* result decoded for test */
37+
int16_t r_decoded[LEN] = {1008, -1008, 1248, 3264, -1312, 8, 32256, -32256};
38+
39+
/**
40+
* @brief 16bit pcm to 8bit alaw
41+
* @param out unsigned 8bit alaw array
42+
* @param in signed 16bit pcm array
43+
* @param len length of pcm array
44+
* @returns void
45+
*/
46+
void encode(uint8_t *out, int16_t *in, size_t len)
47+
{
48+
uint8_t alaw = 0;
49+
int16_t pcm = 0;
50+
int32_t sign = 0;
51+
int32_t abcd = 0;
52+
int32_t eee = 0;
53+
int32_t mask = 0;
54+
for (size_t i = 0; i < len; i++)
55+
{
56+
pcm = *in++;
57+
/* 0-7 kinds of quantization level from the table above */
58+
eee = 7;
59+
mask = 0x4000; /* 0x4000: '0b0100 0000 0000 0000' */
60+
61+
/* Get sign bit */
62+
sign = (pcm & 0x8000) >> 8;
63+
64+
/* Turn negative pcm to positive */
65+
/* The absolute value of a negative number may be larger than the size
66+
* of the corresponding positive number, so here needs `-pcm -1` after
67+
* taking the opposite number. */
68+
pcm = sign ? (-pcm - 1) : pcm;
69+
70+
/* Get eee and abcd bit */
71+
/* Use mask to locate the first `1` bit and quantization level at the
72+
* same time */
73+
while ((pcm & mask) == 0 && eee > 0)
74+
{
75+
eee--;
76+
mask >>= 1;
77+
}
78+
79+
/* The location of abcd bits is related with quantization level. Check
80+
* the table above to determine how many bits to `>>` to get abcd */
81+
abcd = (pcm >> (eee ? (eee + 3) : 4)) & 0x0f;
82+
83+
/* Put the quantization level number at right bit location to get eee
84+
* bits */
85+
eee <<= 4;
86+
87+
/* Splice results */
88+
alaw = (sign | eee | abcd);
89+
90+
/* The standard specifies that all resulting even bits (LSB
91+
* is even) are inverted before the octet is transmitted. This is to
92+
* provide plenty of 0/1 transitions to facilitate the clock recovery
93+
* process in the PCM receivers. Thus, a silent A-law encoded PCM
94+
* channel has the 8 bit samples coded 0xD5 instead of 0x80 in the
95+
* octets. (Reference from wiki above) */
96+
*out++ = alaw ^ 0xD5;
97+
}
98+
}
99+
100+
/**
101+
* @brief 8bit alaw to 16bit pcm
102+
* @param out signed 16bit pcm array
103+
* @param in unsigned 8bit alaw array
104+
* @param len length of alaw array
105+
* @returns void
106+
*/
107+
void decode(int16_t *out, uint8_t *in, size_t len)
108+
{
109+
uint8_t alaw = 0;
110+
int32_t pcm = 0;
111+
int32_t sign = 0;
112+
int32_t eee = 0;
113+
for (size_t i = 0; i < len; i++)
114+
{
115+
alaw = *in++;
116+
117+
/* Re-toggle toggled bits */
118+
alaw ^= 0xD5;
119+
120+
/* Get sign bit */
121+
sign = alaw & 0x80;
122+
123+
/* Get eee bits */
124+
eee = (alaw & 0x70) >> 4;
125+
126+
/* Get abcd bits and add 1/2 quantization step */
127+
pcm = (alaw & 0x0f) << 4 | 8;
128+
129+
/* If quantization level > 0, there need `1` bit before abcd bits */
130+
pcm += eee ? 0x100 : 0x0;
131+
132+
/* Left shift according quantization level */
133+
pcm <<= eee > 1 ? (eee - 1) : 0;
134+
135+
/* Use the right sign */
136+
*out++ = sign ? -pcm : pcm;
137+
}
138+
}
139+
140+
/**
141+
* @brief Self-test implementations
142+
* @param pcm signed 16bit pcm array
143+
* @param coded unsigned 8bit alaw array
144+
* @param decoded signed 16bit pcm array
145+
* @param len length of test array
146+
* @returns void
147+
*/
148+
static void test(int16_t *pcm, uint8_t *coded, int16_t *decoded, size_t len)
149+
{
150+
/* run encode */
151+
encode(coded, pcm, len);
152+
153+
/* check encode result */
154+
for (size_t i = 0; i < len; i++)
155+
{
156+
assert(coded[i] == r_coded[i]);
157+
}
158+
159+
/* run decode */
160+
decode(decoded, coded, len);
161+
162+
/* check decode result */
163+
for (size_t i = 0; i < len; i++)
164+
{
165+
assert(decoded[i] == r_decoded[i]);
166+
}
167+
}
168+
169+
/**
170+
* @brief Main function
171+
* @param argc commandline argument count (ignored)
172+
* @param argv commandline array of arguments (ignored)
173+
* @returns 0 on exit
174+
*/
175+
int main(int argc, char *argv[])
176+
{
177+
/* output alaw encoded by encode() */
178+
uint8_t coded[LEN];
179+
180+
/* output pcm decoded by decode() from coded[LEN] */
181+
int16_t decoded[LEN];
182+
183+
test(pcm, coded, decoded, LEN); // run self-test implementations
184+
185+
/* print test pcm inputs */
186+
printf("inputs: ");
187+
for (size_t i = 0; i < LEN; i++)
188+
{
189+
printf("%d ", pcm[i]);
190+
}
191+
printf("\n");
192+
193+
/* print encoded alaw */
194+
printf("encode: ");
195+
for (size_t i = 0; i < LEN; i++)
196+
{
197+
printf("%u ", coded[i]);
198+
}
199+
printf("\n");
200+
201+
/* print decoded pcm */
202+
printf("decode: ");
203+
for (size_t i = 0; i < LEN; i++)
204+
{
205+
printf("%d ", decoded[i]);
206+
}
207+
printf("\n");
208+
209+
/* It can be seen that the encoded alaw is smaller than the input PCM, so
210+
* the purpose of compression is achieved. And the decoded PCM is almost the
211+
* same as the original input PCM, which verifies the correctness of the
212+
* decoding. The reason why it is not exactly the same is that there is
213+
* precision loss during encode / decode. */
214+
215+
return 0;
216+
}

0 commit comments

Comments
 (0)