#include "scottie.h" #include "image.h" #include "sstv.h" #include "wav.h" #include const scottie_mode_t SCOTTIE_S1 = { .vis_code = 0x3C, // 60d .width = 320, .height = 256, .separator_ms = 1.5, .sync_ms = 9.0, .sync_porch_ms = 1.5, .scan_ms = 138.240, }; const scottie_mode_t SCOTTIE_S2 = { .vis_code = 0x38, // 56d .width = 320, .height = 256, .separator_ms = 1.5, .sync_ms = 9.0, .sync_porch_ms = 1.5, .scan_ms = 88.064, }; const scottie_mode_t SCOTTIE_DX = { .vis_code = 0x4C, // 76d .width = 320, .height = 256, .separator_ms = 1.5, .sync_ms = 9.0, .sync_porch_ms = 1.5, .scan_ms = 345.600, }; static void scan_line_channel(wav_t *wav, uint8_t *line, int width, int channel, double scan_ms) { double px_ms = scan_ms / width; for (int x = 0; x < width; x++) { uint8_t val = line[x * 3 + channel]; double freq = 1500.0 + val * 3.1372549; sstv_tone(wav, freq, px_ms); } } void scottie_encode_image(wav_t *wav, uint8_t *rgb, const scottie_mode_t *mode) { for (int y = 0; y < mode->height; y++) { uint8_t *line = &rgb[y * mode->width * 3]; if (y == 0) { sstv_tone(wav, 1200.0, mode->sync_ms); } sstv_tone(wav, 1500.0, mode->separator_ms); scan_line_channel(wav, line, mode->width, 1, mode->scan_ms); sstv_tone(wav, 1500.0, mode->separator_ms); scan_line_channel(wav, line, mode->width, 2, mode->scan_ms); sstv_tone(wav, 1200.0, mode->sync_ms); sstv_tone(wav, 1500.0, mode->sync_porch_ms); scan_line_channel(wav, line, mode->width, 0, mode->scan_ms); } } int sstv_encode_scottie(const char *input_image, const char *output_wav, const scottie_mode_t *mode) { image_t img, resized; wav_t wav; if (!image_load(&img, input_image)) return 0; if (!image_resize(&img, &resized, mode->width, mode->height)) { image_free(&img); return 0; } if (!wave_open(&wav, output_wav, 44100)) { image_free(&img); image_free_raw(&resized); return 0; } sstv_vis_header_ex(&wav, mode->vis_code); scottie_encode_image(&wav, resized.data, mode); wave_close(&wav); image_free(&img); image_free_raw(&resized); return 1; }