mirror of
https://github.com/radio95-rnt/fm95.git
synced 2026-02-26 11:22:00 +01:00
FMTools
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
stereo_coder
|
||||
sca_mod
|
||||
build/
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
|
||||
2
.vscode/.server-controller-port.log
vendored
2
.vscode/.server-controller-port.log
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"port": 13452,
|
||||
"time": 1735641535890,
|
||||
"time": 1735655261170,
|
||||
"version": "0.0.3"
|
||||
}
|
||||
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"filters.h": "c",
|
||||
"features.h": "c",
|
||||
"constants.h": "c",
|
||||
"oscillator.h": "c"
|
||||
}
|
||||
}
|
||||
33
CMakeLists.txt
Normal file
33
CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# Set the minimum required CMake version
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# Define the project name and language
|
||||
project(FMTools LANGUAGES C)
|
||||
|
||||
# Set the C standard (you can adjust this based on your project needs)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED YES)
|
||||
|
||||
# Find all C source files in the src/ directory
|
||||
file(GLOB SRC_FILES "src/*.c")
|
||||
|
||||
# Find all C source files in the lib/ directory
|
||||
file(GLOB LIB_FILES "lib/*.c")
|
||||
|
||||
# Create a library to hold all object files from lib/
|
||||
add_library(libfm OBJECT ${LIB_FILES})
|
||||
|
||||
# Linker flags for libraries
|
||||
set(LINK_LIBS "-lpulse -lpulse-simple -lm")
|
||||
|
||||
# Loop through each file in src and create an executable
|
||||
foreach(SRC_FILE ${SRC_FILES})
|
||||
# Get the filename without the directory and extension
|
||||
get_filename_component(EXEC_NAME ${SRC_FILE} NAME_WE)
|
||||
|
||||
# Create the executable from each source file
|
||||
add_executable(${EXEC_NAME} ${SRC_FILE})
|
||||
|
||||
# Link the necessary libraries and object files from lib/
|
||||
target_link_libraries(${EXEC_NAME} PRIVATE libfm ${LINK_LIBS})
|
||||
endforeach()
|
||||
@@ -1,3 +1,6 @@
|
||||
# FMTools
|
||||
FMTools is a repository of apps you can use to make your FM broadcast better, pirate or not this will help you if you don't have something, maybe you want a better stereo encoder? SCA? or even went crazy and decided to do quadrophonic? We have what you need, for RDS just use MiniRDS
|
||||
|
||||
# STCode
|
||||
STCode is a simple stereo encoder for FM, it uses pasimple and math to:
|
||||
- Calculate mono signal ((L+R)/2)
|
||||
@@ -10,3 +13,6 @@ Also nearly no latency, not like Stereo Tool (or mpxgen which doesn't even work)
|
||||
|
||||
# SCAMod
|
||||
SCAMod is a simple FM modulator which can be used to modulate a secondary audio stream, has similiar cpu usage and latency as STCode
|
||||
|
||||
# QDCode
|
||||
QD code is a FM quadrophonic encoder, following the Dorren standard
|
||||
@@ -1 +0,0 @@
|
||||
gcc sca_mod.c -lpulse -lpulse-simple -lm -o sca_mod
|
||||
@@ -1 +0,0 @@
|
||||
gcc stereo_coder.c -lpulse -lpulse-simple -lm -o stereo_coder
|
||||
6
lib/constants.h
Normal file
6
lib/constants.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef PI
|
||||
#define PI 3.14159265358979323846
|
||||
#endif
|
||||
#ifndef M_2PI
|
||||
#define M_2PI (3.14159265358979323846 * 2.0)
|
||||
#endif
|
||||
41
lib/filters.c
Normal file
41
lib/filters.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "filters.h"
|
||||
|
||||
void init_emphasis(Emphasis *pe, float tau, float sample_rate) {
|
||||
pe->prev_sample = 0.0f;
|
||||
pe->alpha = exp(-1 / (tau * sample_rate));
|
||||
}
|
||||
|
||||
float apply_pre_emphasis(Emphasis *pe, float sample) {
|
||||
float audio = sample-pe->alpha*pe->prev_sample;
|
||||
pe->prev_sample = audio;
|
||||
return audio*2;
|
||||
}
|
||||
|
||||
void init_low_pass_filter(LowPassFilter *lp, float cutoff_frequency, float sample_rate) {
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
for (int j = 0; j < FIR_PHASES; j++) {
|
||||
int mi = i * FIR_PHASES + j + 1;
|
||||
float sincpos = mi - (((FIR_TAPS * FIR_PHASES) + 1.0f) / 2.0f);
|
||||
float firlowpass = (sincpos == 0.0f) ? 1.0f : sinf(M_2PI * cutoff_frequency * sincpos / sample_rate) / (PI * sincpos);
|
||||
float window = 0.54f - 0.46f * cosf(M_2PI * mi / (FIR_TAPS * FIR_PHASES)); // Hamming window
|
||||
lp->low_pass_fir[j][i] = firlowpass * window;
|
||||
}
|
||||
}
|
||||
memset(lp->sample_buffer, 0, sizeof(lp->sample_buffer));
|
||||
lp->buffer_index = 0;
|
||||
}
|
||||
|
||||
float apply_low_pass_filter(LowPassFilter *lp, float sample) {
|
||||
// Update the sample buffer
|
||||
lp->sample_buffer[lp->buffer_index] = sample;
|
||||
lp->buffer_index = (lp->buffer_index + 1) % FIR_TAPS;
|
||||
|
||||
// Apply the filter
|
||||
float result = 0.0f;
|
||||
int index = lp->buffer_index;
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
result += lp->low_pass_fir[0][i] * lp->sample_buffer[index];
|
||||
index = (index + 1) % FIR_TAPS;
|
||||
}
|
||||
return result*6;
|
||||
}
|
||||
23
lib/filters.h
Normal file
23
lib/filters.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "math.h"
|
||||
#include <string.h>
|
||||
#include "constants.h"
|
||||
|
||||
#define FIR_PHASES 32
|
||||
#define FIR_TAPS 32
|
||||
|
||||
typedef struct {
|
||||
float alpha;
|
||||
float prev_sample;
|
||||
} Emphasis;
|
||||
|
||||
void init_emphasis(Emphasis *pe, float tau, float sample_rate);
|
||||
float apply_pre_emphasis(Emphasis *pe, float sample);
|
||||
|
||||
typedef struct {
|
||||
float low_pass_fir[FIR_PHASES][FIR_TAPS];
|
||||
float sample_buffer[FIR_TAPS];
|
||||
int buffer_index;
|
||||
} LowPassFilter;
|
||||
|
||||
void init_low_pass_filter(LowPassFilter *lp, float cutoff_frequency, float sample_rate);
|
||||
float apply_low_pass_filter(LowPassFilter *lp, float sample);
|
||||
29
lib/oscillator.c
Normal file
29
lib/oscillator.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "oscillator.h"
|
||||
|
||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate) {
|
||||
osc->phase = 0.0f;
|
||||
osc->phase_increment = (M_2PI * frequency) / sample_rate;
|
||||
osc->sample_rate = sample_rate;
|
||||
}
|
||||
|
||||
void change_oscillator_frequency(Oscillator *osc, float frequency) {
|
||||
osc->phase_increment = (M_2PI * frequency) / osc->sample_rate;
|
||||
}
|
||||
|
||||
float get_oscillator_sin_sample(Oscillator *osc) {
|
||||
float sample = sinf(osc->phase);
|
||||
osc->phase += osc->phase_increment;
|
||||
if (osc->phase >= M_2PI) {
|
||||
osc->phase -= M_2PI;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
float get_oscillator_cos_sample(Oscillator *osc) {
|
||||
float sample = cosf(osc->phase);
|
||||
osc->phase += osc->phase_increment;
|
||||
if (osc->phase >= M_2PI) {
|
||||
osc->phase -= M_2PI;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
13
lib/oscillator.h
Normal file
13
lib/oscillator.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "constants.h"
|
||||
#include "math.h"
|
||||
|
||||
typedef struct {
|
||||
float phase;
|
||||
float phase_increment;
|
||||
float sample_rate;
|
||||
} Oscillator;
|
||||
|
||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate);
|
||||
void change_oscillator_frequency(Oscillator *osc, float frequency);
|
||||
float get_oscillator_sin_sample(Oscillator *osc);
|
||||
float get_oscillator_cos_sample(Oscillator *osc);
|
||||
2
src/features.h
Normal file
2
src/features.h
Normal file
@@ -0,0 +1,2 @@
|
||||
// #define PREEMPHASIS
|
||||
#define LPF
|
||||
@@ -8,9 +8,12 @@
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../lib/constants.h"
|
||||
#include "../lib/oscillator.h"
|
||||
#include "../lib/filters.h"
|
||||
|
||||
// Features
|
||||
#define PREEMPHASIS
|
||||
#define LPF
|
||||
#include "features.h"
|
||||
|
||||
#define SAMPLE_RATE 192000 // Don't go lower than 182 KHz (91*2)
|
||||
|
||||
@@ -20,7 +23,7 @@
|
||||
#define CLIPPER_THRESHOLD 0.425 // Adjust this as needed
|
||||
|
||||
#define MONO_VOLUME 0.45f // L+R Signal
|
||||
#define PILOT_VOLUME 0.0225f // 19 KHz Pilot
|
||||
#define PILOT_VOLUME 0.0175f // 19 KHz Pilot
|
||||
#define SIN38_VOLUME 0.35f
|
||||
#define COS38_VOLUME 0.35f
|
||||
#define SIN76_VOLUME 0.35f
|
||||
@@ -54,88 +57,6 @@ void uninterleave(const float *input, float *front_left, float *front_right, flo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define FIR_PHASES 32
|
||||
#define FIR_TAPS 32
|
||||
|
||||
#define PI 3.14159265358979323846
|
||||
#define M_2PI (3.14159265358979323846 * 2.0)
|
||||
|
||||
// Track phase continuously to maintain frequency accuracy
|
||||
typedef struct {
|
||||
float phase;
|
||||
float phase_increment;
|
||||
} Oscillator;
|
||||
|
||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate) {
|
||||
osc->phase = 0.0f;
|
||||
osc->phase_increment = (M_2PI * frequency) / sample_rate;
|
||||
}
|
||||
|
||||
float get_next_sample(Oscillator *osc) {
|
||||
float sample = sinf(osc->phase);
|
||||
osc->phase += osc->phase_increment;
|
||||
if (osc->phase >= M_2PI) {
|
||||
osc->phase -= M_2PI;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
#ifdef PREEMPHASIS
|
||||
typedef struct {
|
||||
float alpha;
|
||||
float prev_sample;
|
||||
} Emphasis;
|
||||
|
||||
void init_emphasis(Emphasis *pe, float sample_rate) {
|
||||
pe->prev_sample = 0.0f;
|
||||
pe->alpha = exp(-1 / (PREEMPHASIS_TAU * sample_rate));
|
||||
}
|
||||
|
||||
float apply_pre_emphasis(Emphasis *pe, float sample) {
|
||||
float audio = sample-pe->alpha*pe->prev_sample;
|
||||
pe->prev_sample = audio;
|
||||
return audio*2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LPF
|
||||
typedef struct {
|
||||
float low_pass_fir[FIR_PHASES][FIR_TAPS];
|
||||
float sample_buffer[FIR_TAPS];
|
||||
int buffer_index;
|
||||
} LowPassFilter;
|
||||
|
||||
void init_low_pass_filter(LowPassFilter *lp, float sample_rate) {
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
for (int j = 0; j < FIR_PHASES; j++) {
|
||||
int mi = i * FIR_PHASES + j + 1;
|
||||
float sincpos = mi - (((FIR_TAPS * FIR_PHASES) + 1.0f) / 2.0f);
|
||||
float firlowpass = (sincpos == 0.0f) ? 1.0f : sinf(M_2PI * LPF_CUTOFF * sincpos / sample_rate) / (PI * sincpos);
|
||||
float window = 0.54f - 0.46f * cosf(M_2PI * mi / (FIR_TAPS * FIR_PHASES)); // Hamming window
|
||||
lp->low_pass_fir[j][i] = firlowpass * window;
|
||||
}
|
||||
}
|
||||
memset(lp->sample_buffer, 0, sizeof(lp->sample_buffer));
|
||||
lp->buffer_index = 0;
|
||||
}
|
||||
|
||||
float apply_low_pass_filter(LowPassFilter *lp, float sample) {
|
||||
// Update the sample buffer
|
||||
lp->sample_buffer[lp->buffer_index] = sample;
|
||||
lp->buffer_index = (lp->buffer_index + 1) % FIR_TAPS;
|
||||
|
||||
// Apply the filter
|
||||
float result = 0.0f;
|
||||
int index = lp->buffer_index;
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
result += lp->low_pass_fir[0][i] * lp->sample_buffer[index];
|
||||
index = (index + 1) % FIR_TAPS;
|
||||
}
|
||||
return result*6;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stop(int signum) {
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal. Cleaning up...\n");
|
||||
@@ -207,17 +128,17 @@ int main() {
|
||||
init_oscillator(&pilot_osc, 19000.0, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier
|
||||
#ifdef PREEMPHASIS
|
||||
Emphasis preemp_lf, preemp_lr, preemp_rf, preemp_rr;
|
||||
init_emphasis(&preemp_lf, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_lr, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_rf, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_rr, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_lf, PREEMPHASIS_TAU, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_lr, PREEMPHASIS_TAU, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_rf, PREEMPHASIS_TAU, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_rr, PREEMPHASIS_TAU, SAMPLE_RATE);
|
||||
#endif
|
||||
#ifdef LPF
|
||||
LowPassFilter lpf_lf, lpf_lr, lpf_rf, lpf_rr;
|
||||
init_low_pass_filter(&lpf_lf, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_lr, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_rf, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_rr, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_lf, LPF_CUTOFF, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_lr, LPF_CUTOFF, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_rf, LPF_CUTOFF, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_rr, LPF_CUTOFF, SAMPLE_RATE);
|
||||
#endif
|
||||
|
||||
signal(SIGINT, stop);
|
||||
@@ -238,7 +159,7 @@ int main() {
|
||||
float sin38 = sinf((pilot_osc.phase+(0.5*PI))*2);
|
||||
float cos38 = cosf((pilot_osc.phase+(0.5*PI))*2);
|
||||
float sin76 = sinf((pilot_osc.phase+(0.5*PI))*4);
|
||||
float pilot = get_next_sample(&pilot_osc);
|
||||
float pilot = get_oscillator_sin_sample(&pilot_osc);
|
||||
float lf_in = left_front[i];
|
||||
float lr_in = left_rear[i];
|
||||
float rf_in = right_front[i];
|
||||
@@ -6,9 +6,12 @@
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../lib/constants.h"
|
||||
#include "../lib/oscillator.h"
|
||||
#include "../lib/filters.h"
|
||||
|
||||
// Features
|
||||
// #define PREEMPHASIS
|
||||
#define LPF
|
||||
#include "features.h"
|
||||
|
||||
#define SAMPLE_RATE 192000
|
||||
|
||||
@@ -41,90 +44,6 @@ float clip(float sample) {
|
||||
}
|
||||
}
|
||||
|
||||
#define FIR_PHASES 32
|
||||
#define FIR_TAPS 32
|
||||
|
||||
#define PI 3.14159265358979323846
|
||||
#define M_2PI (3.14159265358979323846 * 2.0)
|
||||
|
||||
// Track phase continuously to maintain frequency accuracy
|
||||
typedef struct {
|
||||
float phase;
|
||||
float frequency;
|
||||
float sample_rate;
|
||||
} Oscillator;
|
||||
|
||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate) {
|
||||
osc->phase = 0.0f;
|
||||
osc->frequency = frequency;
|
||||
osc->sample_rate = sample_rate;
|
||||
}
|
||||
|
||||
float get_next_sample(Oscillator *osc) {
|
||||
float phase_increment = (M_2PI * osc->frequency) / osc->sample_rate; // If you want to have dynamic frequency changing you have to compute this every sample
|
||||
float sample = sinf(osc->phase);
|
||||
osc->phase += phase_increment;
|
||||
if (osc->phase >= M_2PI) {
|
||||
osc->phase -= M_2PI;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
#ifdef PREEMPHASIS
|
||||
typedef struct {
|
||||
float alpha;
|
||||
float prev_sample;
|
||||
} Emphasis;
|
||||
|
||||
void init_emphasis(Emphasis *pe, float sample_rate) {
|
||||
pe->prev_sample = 0.0f;
|
||||
pe->alpha = exp(-1 / (PREEMPHASIS_TAU * sample_rate));
|
||||
}
|
||||
|
||||
float apply_pre_emphasis(Emphasis *pe, float sample) {
|
||||
float audio = sample-pe->alpha*pe->prev_sample;
|
||||
pe->prev_sample = audio;
|
||||
return audio*2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LPF
|
||||
typedef struct {
|
||||
float low_pass_fir[FIR_PHASES][FIR_TAPS];
|
||||
float sample_buffer[FIR_TAPS];
|
||||
int buffer_index;
|
||||
} LowPassFilter;
|
||||
|
||||
void init_low_pass_filter(LowPassFilter *lp, float sample_rate) {
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
for (int j = 0; j < FIR_PHASES; j++) {
|
||||
int mi = i * FIR_PHASES + j + 1;
|
||||
float sincpos = mi - (((FIR_TAPS * FIR_PHASES) + 1.0f) / 2.0f);
|
||||
float firlowpass = (sincpos == 0.0f) ? 1.0f : sinf(M_2PI * LPF_CUTOFF * sincpos / sample_rate) / (PI * sincpos);
|
||||
float window = 0.54f - 0.46f * cosf(M_2PI * mi / (FIR_TAPS * FIR_PHASES)); // Hamming window
|
||||
lp->low_pass_fir[j][i] = firlowpass * window;
|
||||
}
|
||||
}
|
||||
memset(lp->sample_buffer, 0, sizeof(lp->sample_buffer));
|
||||
lp->buffer_index = 0;
|
||||
}
|
||||
|
||||
float apply_low_pass_filter(LowPassFilter *lp, float sample) {
|
||||
// Update the sample buffer
|
||||
lp->sample_buffer[lp->buffer_index] = sample;
|
||||
lp->buffer_index = (lp->buffer_index + 1) % FIR_TAPS;
|
||||
|
||||
// Apply the filter
|
||||
float result = 0.0f;
|
||||
int index = lp->buffer_index;
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
result += lp->low_pass_fir[0][i] * lp->sample_buffer[index];
|
||||
index = (index + 1) % FIR_TAPS;
|
||||
}
|
||||
return result*6;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stop(int signum) {
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal. Cleaning up...\n");
|
||||
@@ -192,11 +111,11 @@ int main() {
|
||||
init_oscillator(&osc, FREQUENCY, SAMPLE_RATE);
|
||||
#ifdef PREEMPHASIS
|
||||
Emphasis preemp;
|
||||
init_emphasis(&preemp, SAMPLE_RATE);
|
||||
init_emphasis(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE);
|
||||
#endif
|
||||
#ifdef LPF
|
||||
LowPassFilter lpf;
|
||||
init_low_pass_filter(&lpf, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf, LPF_CUTOFF, SAMPLE_RATE);
|
||||
#endif
|
||||
|
||||
signal(SIGINT, stop);
|
||||
@@ -231,8 +150,8 @@ int main() {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
osc.frequency = (FREQUENCY+(current_input*DEVIATION));
|
||||
signal[i] = get_next_sample(&osc)*VOLUME;
|
||||
change_oscillator_frequency(&osc, (FREQUENCY+(current_input*DEVIATION)));
|
||||
signal[i] = get_oscillator_sin_sample(&osc)*VOLUME;
|
||||
}
|
||||
|
||||
if (pa_simple_write(output_device, signal, sizeof(signal), NULL) < 0) {
|
||||
@@ -6,9 +6,12 @@
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../lib/constants.h"
|
||||
#include "../lib/oscillator.h"
|
||||
#include "../lib/filters.h"
|
||||
|
||||
// Features
|
||||
// #define PREEMPHASIS
|
||||
#define LPF
|
||||
#include "features.h"
|
||||
|
||||
#define SAMPLE_RATE 192000 // Don't go lower than 108 KHz, becuase it (53000*2) and (38000+15000)
|
||||
|
||||
@@ -18,7 +21,7 @@
|
||||
#define CLIPPER_THRESHOLD 0.425 // Adjust this as needed
|
||||
|
||||
#define MONO_VOLUME 0.45f // L+R Signal
|
||||
#define PILOT_VOLUME 0.0225f // 19 KHz Pilot
|
||||
#define PILOT_VOLUME 0.0175f // 19 KHz Pilot
|
||||
#define STEREO_VOLUME 0.35f // L-R signal
|
||||
|
||||
#ifdef PREEMPHASIS
|
||||
@@ -49,87 +52,6 @@ void uninterleave(const float *input, float *left, float *right, size_t num_samp
|
||||
}
|
||||
}
|
||||
|
||||
#define FIR_PHASES 32
|
||||
#define FIR_TAPS 32
|
||||
|
||||
#define PI 3.14159265358979323846
|
||||
#define M_2PI (3.14159265358979323846 * 2.0)
|
||||
|
||||
// Track phase continuously to maintain frequency accuracy
|
||||
typedef struct {
|
||||
float phase;
|
||||
float phase_increment;
|
||||
} Oscillator;
|
||||
|
||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate) {
|
||||
osc->phase = 0.0f;
|
||||
osc->phase_increment = (M_2PI * frequency) / sample_rate;
|
||||
}
|
||||
|
||||
float get_next_sample(Oscillator *osc) {
|
||||
float sample = sinf(osc->phase);
|
||||
osc->phase += osc->phase_increment;
|
||||
if (osc->phase >= M_2PI) {
|
||||
osc->phase -= M_2PI;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
#ifdef PREEMPHASIS
|
||||
typedef struct {
|
||||
float alpha;
|
||||
float prev_sample;
|
||||
} Emphasis;
|
||||
|
||||
void init_emphasis(Emphasis *pe, float sample_rate) {
|
||||
pe->prev_sample = 0.0f;
|
||||
pe->alpha = exp(-1 / (PREEMPHASIS_TAU * sample_rate));
|
||||
}
|
||||
|
||||
float apply_pre_emphasis(Emphasis *pe, float sample) {
|
||||
float audio = sample-pe->alpha*pe->prev_sample;
|
||||
pe->prev_sample = audio;
|
||||
return audio*2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LPF
|
||||
typedef struct {
|
||||
float low_pass_fir[FIR_PHASES][FIR_TAPS];
|
||||
float sample_buffer[FIR_TAPS];
|
||||
int buffer_index;
|
||||
} LowPassFilter;
|
||||
|
||||
void init_low_pass_filter(LowPassFilter *lp, float sample_rate) {
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
for (int j = 0; j < FIR_PHASES; j++) {
|
||||
int mi = i * FIR_PHASES + j + 1;
|
||||
float sincpos = mi - (((FIR_TAPS * FIR_PHASES) + 1.0f) / 2.0f);
|
||||
float firlowpass = (sincpos == 0.0f) ? 1.0f : sinf(M_2PI * LPF_CUTOFF * sincpos / sample_rate) / (PI * sincpos);
|
||||
float window = 0.54f - 0.46f * cosf(M_2PI * mi / (FIR_TAPS * FIR_PHASES)); // Hamming window
|
||||
lp->low_pass_fir[j][i] = firlowpass * window;
|
||||
}
|
||||
}
|
||||
memset(lp->sample_buffer, 0, sizeof(lp->sample_buffer));
|
||||
lp->buffer_index = 0;
|
||||
}
|
||||
|
||||
float apply_low_pass_filter(LowPassFilter *lp, float sample) {
|
||||
// Update the sample buffer
|
||||
lp->sample_buffer[lp->buffer_index] = sample;
|
||||
lp->buffer_index = (lp->buffer_index + 1) % FIR_TAPS;
|
||||
|
||||
// Apply the filter
|
||||
float result = 0.0f;
|
||||
int index = lp->buffer_index;
|
||||
for (int i = 0; i < FIR_TAPS; i++) {
|
||||
result += lp->low_pass_fir[0][i] * lp->sample_buffer[index];
|
||||
index = (index + 1) % FIR_TAPS;
|
||||
}
|
||||
return result*6;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stop(int signum) {
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal. Cleaning up...\n");
|
||||
@@ -201,13 +123,13 @@ int main() {
|
||||
init_oscillator(&pilot_osc, 19000.0, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier
|
||||
#ifdef PREEMPHASIS
|
||||
Emphasis preemp_l, preemp_r;
|
||||
init_emphasis(&preemp_l, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_r, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
|
||||
init_emphasis(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
|
||||
#endif
|
||||
#ifdef LPF
|
||||
LowPassFilter lpf_l, lpf_r;
|
||||
init_low_pass_filter(&lpf_l, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_r, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE);
|
||||
init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE);
|
||||
#endif
|
||||
|
||||
signal(SIGINT, stop);
|
||||
@@ -225,7 +147,7 @@ int main() {
|
||||
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
float stereo_carrier = sinf(pilot_osc.phase*2); // Stereo carrier should be a harmonic of the pilot which is in phase, best way to generate the harmonic is to multiply the pilot's phase by two, so it is mathematically impossible for them to not be in phase
|
||||
float pilot = get_next_sample(&pilot_osc); // This is after because if it was before then the stereo would be out of phase by one increment, so [GET STEREO] ([GET PILOT] [INCREMENT PHASE])
|
||||
float pilot = get_oscillator_sin_sample(&pilot_osc); // This is after because if it was before then the stereo would be out of phase by one increment, so [GET STEREO] ([GET PILOT] [INCREMENT PHASE])
|
||||
float l_in = left[i];
|
||||
float r_in = right[i];
|
||||
|
||||
Reference in New Issue
Block a user