Files
audiocodec/encoder.c
2026-07-20 23:18:26 +02:00

375 lines
6.8 KiB
C

//
// Created by bruno on 13. 7. 2026.
//
#include "encoder.h"
#include <SDL.h>
#include <SDL_audio.h>
#include <SDL_stdinc.h>
#include "goertzel.h"
#include "modem_settings.h"
#include "packet.h"
SDL_AudioDeviceID dev;
AudioData encodedAudioData;
Encoder encoder;
const uint8_t syncSymbols[] =
{
2,0,
2,2,
6,2,
1,2
};
static void encoder_set_symbol(
AudioData *audio,
Encoder *enc,
unsigned symbol)
{
/*
symbol:
0 ... number of FSK tones-1
*/
uint16_t freq =
enc->startFreq +
symbol * enc->dataSpacing;
freq = goertzel_bin_freq(
freq,
enc->sampleRate,
enc->symbolSamples
);
SynthVoice *v =
&audio->synthVoices[DATA_VOICE];
v->frequency = freq;
v->phaseStep =
((uint64_t)freq << 32) /
enc->sampleRate;
v->volume = 255;
}
static int encoder_get_symbol(
Encoder *enc)
{
unsigned symbol=0;
for(unsigned i=0;
i<enc->bitsPerSymbol;
i++)
{
if(enc->packetIndex >= enc->packetSize)
return -1;
int bit =
(enc->packet[enc->packetIndex]
>>enc->bitIndex)&1;
symbol |= bit<<i;
enc->bitIndex++;
if(enc->bitIndex==8)
{
enc->bitIndex=0;
enc->packetIndex++;
}
}
return symbol;
}
static void encoder_clock(
Encoder *enc,
AudioData *audio)
{
enc->clockState ^= 1;
SynthVoice *clock =
&audio->synthVoices[0];
uint16_t freq =
enc->clockState ?
enc->clock1 :
enc->clock0;
clock->frequency = freq;
clock->phaseStep =
((uint64_t)freq << 32) /
(enc->sampleRate ? enc->sampleRate : SAMPLE_RATE);
clock->volume = 255;
// Debug: print clock toggle
//printf("ENCODER CLOCK: toggled to %u Hz (state=%d)\n", freq, enc->clockState);
}
void encoder_next_symbol(
Encoder *enc,
AudioData *audio)
{
encoder_clock(enc,audio);
switch(enc->state)
{
case TX_PREAMBLE:
{
unsigned symbol =
enc->preambleSymbols %
enc->toneCount;
encoder_set_symbol(
audio,
enc,
symbol);
enc->preambleSymbols++;
if(enc->preambleSymbols>=PREAMBLE_SYMBOLS)
{
enc->packetIndex=0;
enc->bitIndex=0;
enc->state=TX_PACKET;
}
break;
}
case TX_PACKET:
{
int symbol =
encoder_get_symbol(enc);
if(symbol<0)
{
enc->state=TX_END;
break;
}
encoder_set_symbol(
audio,
enc,
symbol);
break;
}
case TX_END:
stopEncoder(enc);
break;
default:
break;
}
enc->samplesRemaining=
enc->symbolSamples;
}
void startEncoder(Encoder *enc)
{
encoder_build_packet(enc, enc->buffer, enc->bufferSize);
enc->state = TX_PREAMBLE;
enc->transmitting = true;
enc->byteIndex = 0;
enc->bitIndex = 0;
enc->samplesRemaining = 0;
enc->clockState = false;
enc->preambleSymbols = 0;
enc->syncIndex = 0;
}
void stopEncoder(Encoder *enc)
{
enc->transmitting=false;
enc->state=TX_IDLE;
for(int i=0;i<2;i++)
{
encodedAudioData.synthVoices[i].volume=0;
}
}
void audio_callback(void *userdata, Uint8 *stream, int len) {
AudioData *audio = userdata;
Encoder *enc = audio->encoder;
if (enc->state == TX_END ||!enc->transmitting) {
memset(stream, 0, len);
return;
}
float *out = (float *) stream;
int samples = len / sizeof(float);
for (int i = 0; i < samples; i++) {
if (enc->samplesRemaining == 0) {
encoder_next_symbol(enc, audio);
}
enc->samplesRemaining--;
float mix = 0.0f;
int active = 0;
for(unsigned v=0; v<2; v++) {
SynthVoice *voice = &audio->synthVoices[v];
if (voice->volume == 0)
continue;
voice->phase += voice->phaseStep;
float sample = sinf(
((double) voice->phase / 4294967296.0) *
(2.0 * M_PI));
mix += sample * (voice->volume / 255.0f);
active++;
}
float output = active ? mix / (active + 1) : 0.0f;
out[i] = output;
}
}
void initEncoder()
{
memset(&encoder,0,sizeof(Encoder));
encoder.bufferSize = 0;
/*
8-FSK:
3 bits per symbol
8 frequencies
*/
encoder.bitsPerSymbol = 3;
encoder.toneCount = 8;
encoder.byteIndex = 0;
encoder.startFreq =
DEFAULT_START_FREQ;
encoder.endFreq =
DEFAULT_END_FREQ;
uint16_t freqRange =
encoder.endFreq -
encoder.startFreq;
/*
N tones need N-1 intervals
*/
encoder.dataSpacing =
freqRange /
(encoder.toneCount-1);
encoder.symbolSamples =
SAMPLE_RATE /
DEFAULT_SYMBOL_RATE;
encoder.clock0 =
goertzel_bin_freq(
DEFAULT_CLOCK0_FREQ,
SAMPLE_RATE,
encoder.symbolSamples);
encoder.clock1 =
goertzel_bin_freq(
DEFAULT_CLOCK1_FREQ,
SAMPLE_RATE,
encoder.symbolSamples);
encoder.state = TX_IDLE;
encoder.transmitting = false;
}
void initAudioEncoder() {
memset(&encodedAudioData, 0, sizeof(AudioData));
initEncoder();
SDL_AudioSpec spec = {0};
spec.freq = SAMPLE_RATE;
spec.format = AUDIO_F32SYS;
spec.channels = 1;
spec.samples = 960; // requested buffer
spec.callback = audio_callback;
encodedAudioData.encoder = &encoder;
spec.userdata = &encodedAudioData;
SDL_AudioSpec realSpec = {0};
dev = SDL_OpenAudioDevice(NULL, 0, &spec, &realSpec, 0);
printf("Encoder device opened: dev=%u (requested %dHz/%d samples)\n", dev, spec.freq, spec.samples);
if (dev == 0) {
printf("Failed to open audio encoder: %s\n", SDL_GetError());
SDL_Quit();
}
// Update encoder sample rate and symbolSamples based on actual device
encoder.sampleRate = realSpec.freq;
encoder.symbolSamples = realSpec.freq / DEFAULT_SYMBOL_RATE;
encoder.clock0 = goertzel_bin_freq(
DEFAULT_CLOCK0_FREQ,
encoder.sampleRate,
encoder.symbolSamples
);
encoder.clock1 = goertzel_bin_freq(
DEFAULT_CLOCK1_FREQ,
encoder.sampleRate,
encoder.symbolSamples
);
printf("Encoder real spec: freq=%d channels=%d samples=%d -> symbolSamples=%u\n",
realSpec.freq, realSpec.channels, realSpec.samples, encoder.symbolSamples);
SDL_PauseAudioDevice(dev, 0);
}