// // Created by bruno on 13. 7. 2026. // #include "encoder.h" #include #include #include #include "goertzel.h" SDL_AudioDeviceID dev; AudioData encodedAudioData; Encoder encoder; // Internal audio bus float internalBusSamples[INTERNAL_BUFFER_SIZE] = {0}; volatile uint32_t internalBusWrite = 0; volatile uint32_t internalBusRead = 0; #define PREAMBLE_SYMBOLS 128 const uint8_t syncBytes[4] = { 'B', 'R', 'N', 'Q' }; static void encoder_set_bit( AudioData *audio, Encoder *enc, unsigned bitIndex, int bit) { /* float spacing = (float)(enc->endFreq - enc->startFreq) / (enc->bitCount * 2 - 1); */ uint16_t freq = enc->startFreq + enc->dataSpacing * (bitIndex * 2 + bit); freq = goertzel_bin_freq( freq, SAMPLE_RATE, enc->symbolSamples ); SynthVoice *v = &audio->synthVoices[bitIndex + 1]; v->frequency = freq; v->phaseStep = ((uint64_t)freq << 32) / SAMPLE_RATE; v->volume = 255; } static int encoder_get_bit(Encoder *enc) { if (enc->byteIndex >= enc->bufferSize) return -1; int bit = (enc->buffer[enc->byteIndex] >> enc->bitIndex) & 1; enc->bitIndex++; if (enc->bitIndex == 8) { enc->bitIndex = 0; enc->byteIndex++; } return bit; } 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) / SAMPLE_RATE; clock->volume = 255; } void encoder_next_symbol( Encoder *enc, AudioData *audio) { encoder_clock(enc, audio); switch(enc->state) { case TX_PREAMBLE: /* transmit all-zero symbol */ for(unsigned i=0;ibitCount;i++) encoder_set_bit(audio,enc,i,0); enc->preambleSymbols++; if(enc->preambleSymbols >= PREAMBLE_SYMBOLS) { enc->syncIndex = 0; enc->bitIndex = 0; enc->state = TX_SYNC; } break; case TX_SYNC: { uint8_t byte = syncBytes[enc->syncIndex]; for(unsigned i=0;ibitCount;i++) { encoder_set_bit( audio, enc, i, (byte >> i) & 1); } enc->syncIndex++; if(enc->syncIndex == sizeof(syncBytes)) { enc->state = TX_DATA; enc->byteIndex = 0; enc->bitIndex = 0; } break; } case TX_DATA: { int finished = 0; for(unsigned i=0;ibitCount;i++) { int bit = encoder_get_bit(enc); if(bit < 0) { bit = 0; finished++; } encoder_set_bit( audio, enc, i, bit); } if(finished == enc->bitCount) { enc->state = TX_END; } break; } case TX_END: stopEncoder(enc); break; default: break; } enc->samplesRemaining = enc->symbolSamples; } void startEncoder(Encoder *enc) { 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;iencoder; 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 < enc->bitCount + 1; 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; // Write to internal bus for loopback internalBusSamples[internalBusWrite] = output; internalBusWrite = (internalBusWrite + 1) % INTERNAL_BUFFER_SIZE; } } void initEncoder() { memset(&encoder,0,sizeof(Encoder)); encoder.bufferSize = 0; encoder.bitCount = 8; encoder.byteIndex = 0; encoder.startFreq = 1000; encoder.endFreq = 5000; encoder.dataSpacing = 250; encoder.symbolSamples = SAMPLE_RATE / 50; encoder.clock0 = goertzel_bin_freq( 6000, SAMPLE_RATE, encoder.symbolSamples ); encoder.clock1 = goertzel_bin_freq( 6300, 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 = 4096; spec.callback = audio_callback; encodedAudioData.encoder = &encoder; spec.userdata = &encodedAudioData; dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0); printf("Encoder device opened: dev=%u\n", dev); if (dev == 0) { printf("Failed to open audio encoder: %s\n", SDL_GetError()); SDL_Quit(); } SDL_PauseAudioDevice(dev, 0); }