mirror of
https://github.com/radio95-rnt/fm95.git
synced 2026-07-29 15:59:14 +02:00
optimize and fix some stuff
This commit is contained in:
@@ -5,8 +5,7 @@ FM95 is a audio processor for FM, it does:
|
||||
- Pre-Emphasis
|
||||
- Low Pass Filtering
|
||||
- AGC
|
||||
- Stereo (Polar too)
|
||||
- SCA
|
||||
- Stereo
|
||||
- BS412 (mpx power limiter, simplest implementation ever)
|
||||
|
||||
Supports these inputs:
|
||||
@@ -36,11 +35,11 @@ Done!
|
||||
|
||||
## CPU Usage?
|
||||
|
||||
Should run completly fine on a pi 5, fine on a pi 3b (30% cpu, 45% with lpf)
|
||||
Should run completly fine on a pi 5, fine on a pi 3b (~30% cpu)
|
||||
|
||||
## Other Apps
|
||||
|
||||
FM95 also includes some other apps, such as chimer95 which generates GTS tones each half hour, and dcf95 which creates a DCF77 compatible signal, and vban95 now which is a buffered VBAN receiver. And now also SCA generation was moved to sca95 from fm95!
|
||||
FM95 also includes some other apps, such as chimer95 which generates GTS tones each half hour, and vban95 now which is a buffered VBAN receiver. And now also SCA generation was moved to sca95 from fm95!
|
||||
|
||||
## Usage of other projects
|
||||
|
||||
|
||||
@@ -35,10 +35,6 @@ Simple integer, set to 1 to output a sine wave at 400 Hz, which should correspon
|
||||
|
||||
Float value to set the master volume, 1 by default, set to 0 to mute MPX
|
||||
|
||||
### audio_volume
|
||||
|
||||
Float value to set the audio volume, also 1 by default, set to 0 to mute audio itself, but stereo pilot and RDS should remain
|
||||
|
||||
### audio_preamp
|
||||
|
||||
Pre-amplification of the audio, before any filters, set to 1 by default, set to 0 to mute audio
|
||||
|
||||
@@ -5,7 +5,7 @@ void init_stereo_encoder(StereoEncoder* st, uint8_t stereo_ssb, uint8_t multipli
|
||||
st->multiplier = multiplier;
|
||||
st->osc = osc;
|
||||
st->pilot_volume = pilot_volume;
|
||||
st->audio_volume = audio_volume;
|
||||
st->audio_volume = audio_volume * 0.5f;
|
||||
if(stereo_ssb) {
|
||||
init_delay_line(&st->delay_pilot, stereo_ssb*2);
|
||||
init_delay_line(&st->delay, stereo_ssb*2);
|
||||
@@ -15,7 +15,10 @@ void init_stereo_encoder(StereoEncoder* st, uint8_t stereo_ssb, uint8_t multipli
|
||||
|
||||
float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right, float *audio) {
|
||||
float mid = (left+right) * 0.5f;
|
||||
if(!enabled) return mid * st->audio_volume;
|
||||
if(!enabled) {
|
||||
*audio = mid * 2.0f * st->audio_volume;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if(st->stereo_hilbert) mid = delay_line(&st->delay, mid);
|
||||
|
||||
@@ -33,12 +36,12 @@ float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right,
|
||||
}
|
||||
float signalx2 = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier * 2.0f);
|
||||
|
||||
*audio = (mid*st->audio_volume);
|
||||
*audio = mid * st->audio_volume;
|
||||
if(st->stereo_hilbert) {
|
||||
float stereo = (crealf(stereo_hilbert) * signalx2) - (cimagf(stereo_hilbert) * signalx2cos);
|
||||
*audio += (stereo * st->audio_volume);
|
||||
} else *audio += ((side*signalx2) * st->audio_volume);
|
||||
return (signalx1*st->pilot_volume);
|
||||
*audio += stereo * st->audio_volume;
|
||||
} else *audio += (side*signalx2) * st->audio_volume;
|
||||
return signalx1 * st->pilot_volume;
|
||||
}
|
||||
|
||||
void exit_stereo_encoder(StereoEncoder* st) {
|
||||
|
||||
+35
-68
@@ -18,19 +18,9 @@
|
||||
|
||||
#include "../io/audio.h"
|
||||
|
||||
#define DEFAULT_PILOT_VOLUME 0.09f // 9%
|
||||
#define DEFAULT_RDS_VOLUME 0.0475f // 4.75%
|
||||
#define DEFAULT_RDS_VOLUME_STEP 0.9f // 90%, so RDS2 stream 4 is 90% of stream 3 which is 90% of stream 2, which again is 90% of stream 1...
|
||||
|
||||
static volatile sig_atomic_t to_run = 1;
|
||||
static volatile sig_atomic_t to_reload = 0;
|
||||
|
||||
static inline float soft_clip(float x) { return tanhf(x); }
|
||||
|
||||
static inline float soft_clip_drive(float x, float drive) {
|
||||
return tanhf(x * drive) / tanhf(drive);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool rds_on;
|
||||
bool mpx_on;
|
||||
@@ -59,7 +49,6 @@ typedef struct {
|
||||
float mpx_deviation;
|
||||
float audio_deviation;
|
||||
float master_volume;
|
||||
float audio_volume;
|
||||
float audio_preamp;
|
||||
|
||||
uint32_t sample_rate;
|
||||
@@ -104,10 +93,7 @@ typedef struct {
|
||||
} FM95_SetupContext;
|
||||
|
||||
bool compare_dvs(const FM95_DeviceNames *a, const FM95_DeviceNames *b) {
|
||||
return strcmp(a->input, b->input) == 0 &&
|
||||
strcmp(a->output, b->output) == 0 &&
|
||||
strcmp(a->mpx, b->mpx) == 0 &&
|
||||
strcmp(a->rds, b->rds) == 0;
|
||||
return strcmp(a->input, b->input) == 0 && strcmp(a->output, b->output) == 0 && strcmp(a->mpx, b->mpx) == 0 && strcmp(a->rds, b->rds) == 0;
|
||||
}
|
||||
|
||||
float calculate_sharedaudio_volume(const FM95_Volumes volumes, const int rds_streams) {
|
||||
@@ -169,7 +155,7 @@ int run_fm95(const FM95_Config config, FM95_Runtime* runtime) {
|
||||
else if(config.calibration == 3) sample *= (19000/config.mpx_deviation);
|
||||
output[i] = sample*config.master_volume;
|
||||
}
|
||||
if((pulse_error = write_PulseOutputDevice(&runtime->output_device, output, sizeof(output)))) { // get output from the function and assign it into pulse_error, this comment to avoid confusion
|
||||
if((pulse_error = write_PulseOutputDevice(&runtime->output_device, output, sizeof(output)))) {
|
||||
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
@@ -185,6 +171,8 @@ int run_fm95(const FM95_Config config, FM95_Runtime* runtime) {
|
||||
bool mpx_on = config.options.mpx_on;
|
||||
bool rds_on = config.options.rds_on;
|
||||
|
||||
float softclip_norm = config.volumes.makeup / tanhf(config.volumes.drive);
|
||||
|
||||
while (to_run) {
|
||||
if((pulse_error = read_PulseInputDevice(&runtime->input_device, audio_stereo_input, sizeof(audio_stereo_input)))) { // get output from the function and assign it into pulse_error, this comment to avoid confusion
|
||||
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
|
||||
@@ -229,8 +217,8 @@ int run_fm95(const FM95_Config config, FM95_Runtime* runtime) {
|
||||
mod_r = apply_preemphasis(&runtime->preemp_r, mod_r);
|
||||
}
|
||||
|
||||
mod_l = soft_clip_drive(mod_l, config.volumes.drive) * config.volumes.makeup;
|
||||
mod_r = soft_clip_drive(mod_r, config.volumes.drive) * config.volumes.makeup;
|
||||
mod_l = tanhf(mod_l * config.volumes.drive) * softclip_norm;
|
||||
mod_r = tanhf(mod_r * config.volumes.drive) * softclip_norm;
|
||||
|
||||
mpx = stereo_encode(&runtime->stencode, config.stereo, mod_l, mod_r, &audio);
|
||||
|
||||
@@ -250,7 +238,7 @@ int run_fm95(const FM95_Config config, FM95_Runtime* runtime) {
|
||||
|
||||
mpx = bs412_compress(&runtime->bs412, audio, mpx+mpx_in[i]);
|
||||
|
||||
output[i] = soft_clip(mpx)*config.master_volume; // Ensure peak deviation of 75 khz (or the set deviation), assuming we're calibrated correctly
|
||||
output[i] = tanhf(mpx)*config.master_volume; // Ensure peak deviation of 75 khz (or the set deviation), assuming we're calibrated correctly
|
||||
advance_oscillator(&runtime->osc);
|
||||
}
|
||||
|
||||
@@ -296,9 +284,8 @@ static int config_handler(void* user, const char* section, const char* name, con
|
||||
|
||||
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||
|
||||
if (MATCH("fm95", "stereo")) {
|
||||
pconfig->stereo = atoi(value);
|
||||
} else if (MATCH("devices", "input")) {
|
||||
if (MATCH("fm95", "stereo")) pconfig->stereo = atoi(value);
|
||||
else if (MATCH("devices", "input")) {
|
||||
strncpy(dv->input, value, 63);
|
||||
dv->input[63] = '\0';
|
||||
} else if (MATCH("devices", "output")) {
|
||||
@@ -316,47 +303,26 @@ static int config_handler(void* user, const char* section, const char* name, con
|
||||
printf("RDS Streams more than 4? Nuh uh\n");
|
||||
return 0;
|
||||
}
|
||||
} else if (MATCH("fm95", "preemphasis")) {
|
||||
pconfig->preemphasis = atoi(value);
|
||||
} else if (MATCH("fm95", "calibration")) {
|
||||
pconfig->calibration = atoi(value);
|
||||
} else if (MATCH("fm95", "mpx_power")) {
|
||||
pconfig->mpx_power = strtof(value, NULL);
|
||||
} else if (MATCH("fm95", "mpx_deviation")) {
|
||||
pconfig->mpx_deviation = strtof(value, NULL);
|
||||
} else if (MATCH("fm95", "master_volume")) {
|
||||
pconfig->master_volume = strtof(value, NULL);
|
||||
} else if (MATCH("fm95", "audio_volume")) {
|
||||
pconfig->audio_volume = strtof(value, NULL);
|
||||
} else if (MATCH("fm95", "audio_preamp")) {
|
||||
pconfig->audio_preamp = strtof(value, NULL);
|
||||
} else if (MATCH("fm95", "deviation")) {
|
||||
pconfig->audio_deviation = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "agc_target")) {
|
||||
pconfig->agc_target = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "agc_attack")) {
|
||||
pconfig->agc_attack = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "agc_release")) {
|
||||
pconfig->agc_release = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "agc_min")) {
|
||||
pconfig->agc_min = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "agc_max")) {
|
||||
pconfig->agc_max = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "bs412_attack")) {
|
||||
pconfig->bs412_attack = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "bs412_release")) {
|
||||
pconfig->bs412_release = strtof(value, NULL);
|
||||
} else if(MATCH("fm95", "bs412_max")) {
|
||||
pconfig->bs412_max = strtof(value, NULL);
|
||||
} else if(MATCH("advanced", "lpf_order")) {
|
||||
pconfig->lpf_order = atoi(value);
|
||||
} else if(MATCH("advanced", "stereo_ssb")) {
|
||||
pconfig->stereo_ssb = atoi(value);
|
||||
} else if(MATCH("advanced", "preemp_unity")) {
|
||||
pconfig->preemp_unity_freq = strtof(value, NULL);
|
||||
} else if(MATCH("advanced", "sample_rate")) {
|
||||
pconfig->sample_rate = atoi(value);
|
||||
} else if(MATCH("advanced", "lpf_cutoff")) {
|
||||
} else if (MATCH("fm95", "preemphasis")) pconfig->preemphasis = atoi(value);
|
||||
else if (MATCH("fm95", "calibration")) pconfig->calibration = atoi(value);
|
||||
else if (MATCH("fm95", "mpx_power")) pconfig->mpx_power = strtof(value, NULL);
|
||||
else if (MATCH("fm95", "mpx_deviation")) pconfig->mpx_deviation = strtof(value, NULL);
|
||||
else if (MATCH("fm95", "master_volume")) pconfig->master_volume = strtof(value, NULL);
|
||||
else if (MATCH("fm95", "audio_preamp")) pconfig->audio_preamp = strtof(value, NULL);
|
||||
else if (MATCH("fm95", "deviation")) pconfig->audio_deviation = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "agc_target")) pconfig->agc_target = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "agc_attack")) pconfig->agc_attack = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "agc_release")) pconfig->agc_release = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "agc_min")) pconfig->agc_min = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "agc_max")) pconfig->agc_max = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "bs412_attack")) pconfig->bs412_attack = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "bs412_release")) pconfig->bs412_release = strtof(value, NULL);
|
||||
else if(MATCH("fm95", "bs412_max")) pconfig->bs412_max = strtof(value, NULL);
|
||||
else if(MATCH("advanced", "lpf_order")) pconfig->lpf_order = atoi(value);
|
||||
else if(MATCH("advanced", "stereo_ssb")) pconfig->stereo_ssb = atoi(value);
|
||||
else if(MATCH("advanced", "preemp_unity")) pconfig->preemp_unity_freq = strtof(value, NULL);
|
||||
else if(MATCH("advanced", "sample_rate")) pconfig->sample_rate = atoi(value);
|
||||
else if(MATCH("advanced", "lpf_cutoff")) {
|
||||
pconfig->lpf_cutoff = strtof(value, NULL);
|
||||
if(pconfig->lpf_cutoff > (pconfig->sample_rate * 0.5)) {
|
||||
pconfig->lpf_cutoff = (pconfig->sample_rate * 0.5);
|
||||
@@ -491,13 +457,13 @@ void init_runtime(FM95_Runtime* runtime, const FM95_Config config) {
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
printf("fm95 (an FM Processor by radio95) version 2.4\n");
|
||||
printf("fm95 (an FM Processor by radio95) version 2.5\n");
|
||||
|
||||
FM95_Config config = {
|
||||
.volumes = {
|
||||
.pilot = DEFAULT_PILOT_VOLUME,
|
||||
.rds = DEFAULT_RDS_VOLUME,
|
||||
.rds_step = DEFAULT_RDS_VOLUME_STEP,
|
||||
.pilot = 0.09f,
|
||||
.rds = 0.045f,
|
||||
.rds_step = 0.9f,
|
||||
.headroom = 0.05f,
|
||||
.drive = 1.0f,
|
||||
.makeup = 1.0f
|
||||
@@ -513,7 +479,6 @@ int main(int argc, char **argv) {
|
||||
.mpx_deviation = 75000.0f, // for BS412, this is what deviation does the compressor see as peak, so if i set here 150 khz, then the compressor will act as if it was two times louder
|
||||
.audio_deviation = 75000.0f, // another way to set the volume
|
||||
.master_volume = 1.0f, // Volume of everything combined, for calibration
|
||||
.audio_volume = 1.0f, // Volume of the audio, before stereo encoding, before clipper
|
||||
.audio_preamp = 1.0f, // Volume of the audio before the filters
|
||||
|
||||
.sample_rate = 192000, // Sample rate for this whole gizmo to run on
|
||||
@@ -560,6 +525,8 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (config.volumes.drive < 0.01f) config.volumes.drive = 0.01f;
|
||||
|
||||
config.master_volume *= config.audio_deviation/75000.0f;
|
||||
|
||||
config.volumes.audio = calculate_sharedaudio_volume(config.volumes, config.rds_streams);
|
||||
|
||||
Reference in New Issue
Block a user