diff --git a/Makefile b/Makefile index 12c637d..cfb056f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CFLAGS = -Wall -O2 LDFLAGS = -lm TARGET = sstv -SRCS = main.c sstv.c robot36.c image.c wav.c +SRCS = main.c sstv.c robot36.c image.c wav.c martin.c OBJS = $(SRCS:.c=.o) $(TARGET): $(OBJS) diff --git a/main.c b/main.c index 5f268ce..226f581 100644 --- a/main.c +++ b/main.c @@ -1,14 +1,41 @@ #include "sstv.h" +#include "martin.h" #include +#include + +static void usage(const char *prog) { + printf("Usage: %s input_image output.wav\n", prog); + printf("Modes:\n"); + printf(" robot36 - Robot 36 (default)\n"); + printf(" martin1 - Martin M1\n"); + printf(" martin2 - Martin M2\n"); +} int main(int argc, char **argv) { - if (argc < 3) { - printf("Usage: %s input_image output.wav\n", argv[0]); + if (argc < 4) { + usage(argv[0]); return 1; } - if (!sstv_encode_robot36(argv[1], argv[2])) { - printf("Encoding failed\n"); + const char *mode_str = argv[1]; + const char *input_img = argv[2]; + const char *output_wav = argv[3]; + int ok = 0; + + if (strcmp(mode_str, "robot36") == 0) { + ok = sstv_encode_robot36(input_img, output_wav); + } else if (strcmp(mode_str, "martin1") == 0) { + ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M1); + } else if (strcmp(mode_str, "martin2") == 0) { + ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M2); + } else { + fprintf(stderr, "Unknown mode: %s\n", mode_str); + usage(argv[0]); + return 1; + } + + if (!ok) { + fprintf(stderr, "Encoding failed.\n"); return 1; } diff --git a/martin.c b/martin.c new file mode 100644 index 0000000..c87cfdb --- /dev/null +++ b/martin.c @@ -0,0 +1,78 @@ +#include "martin.h" +#include "image.h" +#include "sstv.h" +#include "wav.h" +#include + +const martin_mode_t MARTIN_M1 = { + .vis_code = 0x2C, // hex (dd -> 44d) + .width = 320, + .height = 256, + .sync_ms = 4.862, + .sync_porch_ms = 0.572, + .separator_ms = 0.572, + .scan_ms = 146.432, // per channel , per line +}; + +const martin_mode_t MARTIN_M2 = { + .vis_code = 0x28, // hex (dd -> 40d) + .width = 320, + .height = 256, + .sync_ms = 4.862, + .sync_porch_ms = 0.572, + .separator_ms = 0.572, + .scan_ms = 73.216, // half of M1 +}; + +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 martin_encode_image(wav_t *wav, uint8_t *rgb, const martin_mode_t *mode) { + for (int y = 0; y < mode->height; y++) { + uint8_t *line = &rgb[y * mode->width * 3]; + + sstv_tone(wav, 1200.0, mode->sync_ms); // sync pulse + sstv_tone(wav, 1500.0, mode->sync_porch_ms); // sync porch + scan_line_channel(wav, line, mode->width, 1, mode->scan_ms); // G + sstv_tone(wav, 1500.0, mode->separator_ms); + scan_line_channel(wav, line, mode->width, 2, mode->scan_ms); // B + sstv_tone(wav, 1500.0, mode->separator_ms); + scan_line_channel(wav, line, mode->width, 0, mode->scan_ms); // R + sstv_tone(wav, 1500.0, mode->separator_ms); + } +} + +int sstv_encode_martin(const char *input_image, const char *output_wav, const martin_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); + martin_encode_image(&wav, resized.data, mode); + wave_close(&wav); + + image_free(&img); + image_free_raw(&resized); + + return 1; +} diff --git a/martin.h b/martin.h new file mode 100644 index 0000000..7ec292c --- /dev/null +++ b/martin.h @@ -0,0 +1,23 @@ +#ifndef MARTIN_H +#define MARTIN_H + +#include "wav.h" +#include + +typedef struct { + uint8_t vis_code; + int width; + int height; + double sync_ms; + double sync_porch_ms; + double separator_ms; + double scan_ms; +} martin_mode_t; + +extern const martin_mode_t MARTIN_M1; +extern const martin_mode_t MARTIN_M2; + +void martin_encode_image(wav_t *wav, uint8_t *rgb, const martin_mode_t *mode); +int sstv_encode_martin(const char *input_image, const char *output_wav, const martin_mode_t *mode); + +#endif // !MARTIN_H diff --git a/sstv.c b/sstv.c index 468b8d8..504b188 100644 --- a/sstv.c +++ b/sstv.c @@ -5,16 +5,19 @@ #include #include -#define PI 3.14159265358979 +#define PI 3.14159265358979 #define AMPLITUDE 12000 -static double phase = 0.0; +static double phase = 0.0; +static double sample_accum = 0.0; // accumulator void sstv_tone(wav_t *wav, double freq, double duration_ms) { - uint32_t total_samples = (uint32_t)((duration_ms / 1000.0) * wav->sample_rate); + sample_accum += (duration_ms / 1000.0) * wav->sample_rate; + uint32_t total_samples = (uint32_t)sample_accum; + sample_accum -= total_samples; + double step = 2.0 * PI * freq / wav->sample_rate; for (uint32_t i = 0; i < total_samples; i++) { - double step = 2.0 * PI * freq / wav->sample_rate; phase += step; if (phase > 2.0 * PI) phase -= 2.0 * PI; @@ -24,31 +27,36 @@ void sstv_tone(wav_t *wav, double freq, double duration_ms) { } } -void sstv_vis_header(wav_t *wav) { - uint8_t vis = 0x08; - uint8_t parity = 0; - +void sstv_vis_header_ex(wav_t *wav, uint8_t vis_code) { + // reset accumulator + phase = 0.0; + sample_accum = 0.0; + + /* preamble */ sstv_tone(wav, 1900, 300); - sstv_tone(wav, 1200, 10); + sstv_tone(wav, 1200, 10); sstv_tone(wav, 1900, 300); sstv_tone(wav, 1200, 30); + uint8_t parity = 0; for (int i = 0; i < 7; i++) { - uint8_t bit = (vis >> i) & 1; - if (bit) { - sstv_tone(wav, 1100, 30); - parity ^= 1; - } else { - sstv_tone(wav, 1300, 30); - } + uint8_t bit = (vis_code >> i) & 1; + sstv_tone(wav, bit ? 1100 : 1300, 30); + parity ^= bit; } + /* parity bit */ sstv_tone(wav, parity ? 1100 : 1300, 30); + /* stop bit */ sstv_tone(wav, 1200, 30); } +void sstv_vis_header(wav_t *wav) { + sstv_vis_header_ex(wav, 0x08); +} + int sstv_encode_robot36(const char *input_image, const char *output_wav) { image_t img, resized; wav_t wav; diff --git a/sstv.h b/sstv.h index 6a82204..6cdfab7 100644 --- a/sstv.h +++ b/sstv.h @@ -5,7 +5,11 @@ #include void sstv_tone(wav_t *wav, double freq, double duration_ms); + void sstv_vis_header(wav_t *wav); -int sstv_encode_robot36(const char *input_image, const char *output_wav); + +void sstv_vis_header_ex(wav_t *wav, uint8_t vis_code); + +int sstv_encode_robot36(const char *input_image, const char *output_wav); #endif // !SSTV_H