parent
9de121109d
commit
2a092b8cf7
@ -1,3 +1,3 @@ |
||||
AUTOMAKE_OPTIONS = foreign
|
||||
SUBDIRS = common anetz bnetz nmt
|
||||
SUBDIRS = common anetz bnetz nmt test
|
||||
|
||||
|
@ -1,11 +1,21 @@ |
||||
typedef struct compander { |
||||
double step_up; |
||||
double step_down; |
||||
double envelop_c; |
||||
double envelop_e; |
||||
struct { |
||||
double unaffected; |
||||
double step_up; |
||||
double step_down; |
||||
double peak; |
||||
double envelope; |
||||
} c; |
||||
struct { |
||||
double unaffected; |
||||
double step_up; |
||||
double step_down; |
||||
double peak; |
||||
double envelope; |
||||
} e; |
||||
} compander_t; |
||||
|
||||
void init_compander(compander_t *state, int samplerate, double attack_ms, double recovery_ms); |
||||
void init_compander(compander_t *state, int samplerate, double attack_ms, double recovery_ms, int unaffected_level); |
||||
void compress_audio(compander_t *state, int16_t *samples, int num); |
||||
void expand_audio(compander_t *state, int16_t *samples, int num); |
||||
|
||||
|
@ -0,0 +1,12 @@ |
||||
AM_CPPFLAGS = -Wall -g $(all_includes)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
test_compander
|
||||
|
||||
test_compander_SOURCES = test_compander.c
|
||||
|
||||
test_compander_LDADD = \
|
||||
$(COMMON_LA) \
|
||||
$(top_builddir)/src/common/libcommon.a \
|
||||
-lm
|
||||
|
@ -0,0 +1,122 @@ |
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <math.h> |
||||
#include <string.h> |
||||
#include "../common/compander.h" |
||||
|
||||
#define level2db(level) (20 * log10(level)) |
||||
#define db2level(db) pow(10, (double)db / 20.0) |
||||
|
||||
#define SAMPLERATE 48000 |
||||
#define ATTACK_MS 15.0 |
||||
#define RECOVERY_MS 15.0 |
||||
#define UNAFFECTED 10000.0 |
||||
|
||||
static double test_frequency[3] = { 2000.0, 4000.0, 1000.0 }; |
||||
|
||||
static int16_t samples_4db[SAMPLERATE]; |
||||
static int16_t samples_16db[SAMPLERATE]; |
||||
static int16_t samples_2db[SAMPLERATE]; |
||||
static int16_t samples_8db[SAMPLERATE]; |
||||
static int16_t samples_0db[SAMPLERATE]; |
||||
|
||||
/* generate 2 samples: one with -4 dB, the other with -16 dB */ |
||||
static void generate_test_sample(double test_frequency) |
||||
{ |
||||
int i; |
||||
double value; |
||||
|
||||
for (i = 0; i < SAMPLERATE; i++) { |
||||
value = cos(2.0 * M_PI * test_frequency / (double)SAMPLERATE * i); |
||||
samples_4db[i] = (int)(UNAFFECTED * value * db2level(-4)); |
||||
samples_16db[i] = (int)(UNAFFECTED * value * db2level(-16)); |
||||
samples_2db[i] = (int)(UNAFFECTED * value * db2level(-2)); |
||||
samples_8db[i] = (int)(UNAFFECTED * value * db2level(-8)); |
||||
samples_0db[i] = (int)(UNAFFECTED * value); |
||||
} |
||||
} |
||||
|
||||
static void check_level(int16_t *samples, double duration, const char *desc, double target_db) |
||||
{ |
||||
int i; |
||||
int last = 0, envelop = 0; |
||||
int up = 0; |
||||
double factor; |
||||
|
||||
int when = (int)((double)SAMPLERATE + (double)SAMPLERATE * duration / 1000.0 + 0.5); |
||||
|
||||
for (i = 0; i < when; i++) { |
||||
if (last < samples[i]) { |
||||
up = 1; |
||||
} else if (last > samples[i]) { |
||||
if (up) { |
||||
envelop = last; |
||||
} |
||||
up = 0; |
||||
} |
||||
#if 0 |
||||
if (i >= (SAMPLERATE-(SAMPLERATE/100)) && (i % (SAMPLERATE/(SAMPLERATE/10))) == 0) |
||||
printf("%s: envelop = %.4f (when=%d)\n", desc, level2db((double)envelop / UNAFFECTED), i); |
||||
#endif |
||||
last = samples[i]; |
||||
} |
||||
factor = (envelop / UNAFFECTED) / db2level(target_db); |
||||
printf("%s: envelop after the instance of %.1f ms is %.4f db factor =%.4f\n", desc, duration, level2db((double)envelop / UNAFFECTED), factor); |
||||
} |
||||
|
||||
int main(void) |
||||
{ |
||||
compander_t cstate; |
||||
int16_t samples[SAMPLERATE * 2]; |
||||
int f; |
||||
|
||||
init_compander(&cstate, SAMPLERATE, ATTACK_MS, RECOVERY_MS, UNAFFECTED); |
||||
|
||||
for (f = 0; f < 3; f++) { |
||||
/* -16 and -4 dB */ |
||||
generate_test_sample(test_frequency[f]); |
||||
|
||||
/* low to high transition */ |
||||
memcpy(samples, samples_16db, SAMPLERATE * 2); |
||||
memcpy(samples + SAMPLERATE, samples_4db, SAMPLERATE * 2); |
||||
|
||||
compress_audio(&cstate, samples, SAMPLERATE * 2); |
||||
|
||||
check_level(samples, ATTACK_MS, "compressor attack", -2.0); |
||||
|
||||
/* high to low transition */ |
||||
memcpy(samples, samples_4db, SAMPLERATE * 2); |
||||
memcpy(samples + SAMPLERATE, samples_16db, SAMPLERATE * 2); |
||||
|
||||
compress_audio(&cstate, samples, SAMPLERATE * 2); |
||||
|
||||
check_level(samples, RECOVERY_MS, "compressor recovery", -8.0); |
||||
|
||||
/* low to high transition */ |
||||
memcpy(samples, samples_8db, SAMPLERATE * 2); |
||||
memcpy(samples + SAMPLERATE, samples_2db, SAMPLERATE * 2); |
||||
|
||||
expand_audio(&cstate, samples, SAMPLERATE * 2); |
||||
|
||||
check_level(samples, ATTACK_MS, "expander attack", -4.0); |
||||
|
||||
/* high to low transition */ |
||||
memcpy(samples, samples_2db, SAMPLERATE * 2); |
||||
memcpy(samples + SAMPLERATE, samples_8db, SAMPLERATE * 2); |
||||
|
||||
expand_audio(&cstate, samples, SAMPLERATE * 2); |
||||
|
||||
check_level(samples, RECOVERY_MS, "expander recovery", -16.0); |
||||
|
||||
/* 0 DB */ |
||||
memcpy(samples, samples_0db, SAMPLERATE * 2); |
||||
memcpy(samples + SAMPLERATE, samples_0db, SAMPLERATE * 2); |
||||
|
||||
check_level(samples, RECOVERY_MS, "unaffected level", 0.0); |
||||
|
||||
puts(""); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
Loading…
Reference in new issue