libsstvenc
Asynchronous Analogue SSTV encoder
Loading...
Searching...
No Matches
png-to-sstv.c
Go to the documentation of this file.
1/*
2 * © Stuart Longland VK4MSL
3 * SPDX-License-Identifier: MIT
4 */
5
6#include <gd.h>
7#include <getopt.h>
9#include <libsstvenc/sunau.h>
10#include <libsstvenc/yuv.h>
11#include <stdio.h>
12#include <string.h>
13
14static void show_modes(void) {
15 uint8_t idx = 0;
16 const struct sstvenc_mode* mode = sstvenc_get_mode_by_idx(idx);
17
18 printf("MODE : Description "
19 "Wdth x Hght CS. TX Time*\n"
20 "-------- : -------------------------------- "
21 "----------- ---- ------------\n");
22
23 while (mode != NULL) {
24 const char* cspace;
27 cspace = "MONO";
28 break;
30 cspace = "RGB";
31 break;
34 cspace = "YUV";
35 break;
36 }
37 printf("%-8s : %-32s %4d x %4d %-4s %7.3f sec\n", mode->name,
38 mode->description, mode->width, mode->height, cspace,
39 sstvenc_mode_get_txtime(mode, NULL) * 1.0e-9);
40
41 idx++;
42 mode = sstvenc_get_mode_by_idx(idx);
43 }
44
45 printf("\n* not including FSK ID\n");
46}
47
48static void show_usage(const char* prog_name) {
49 printf("Usage: %s [options] input.png output.au\n"
50 "Options:\n"
51 " {--bits | -B} BITS: set the number of bits per sample\n"
52 " 8: 8-bit signed integer\n"
53 " 16: 16-bit signed integer (default)\n"
54 " 32: 32-bit signed integer\n"
55 " 32s: also 32-bit signed integer (alias)\n"
56 " 32f: 32-bit IEEE-754 float\n"
57 " 64: 64-bit IEEE-754 float\n"
58 " {--chan | -C} CHAN: select audio channels\n"
59 " 1: mono output (default)\n"
60 " m: mono output (alias)\n"
61 " 2: stereo output -- use both channels\n"
62 " s: stereo output (alias) -- use both channels\n"
63 " l: stereo output -- use left channel\n"
64 " r: stereo output -- use right channel\n"
65 " {--mode | -M} MODE: choose SSTV mode (see below)\n"
66 " {--rate | -R} RATE: sample rate in Hz\n"
67 " {--fsk-id | -f} FSKID: Set the FSK ID\n"
68 "\n"
69 "Supported SSTV modes:\n",
70 prog_name);
71
72 show_modes();
73}
74
75int main(int argc, char* argv[]) {
76 const char* opt_fsk_id = NULL;
77 const char* opt_channels = "1";
78 const char* opt_mode = "M1";
79 const char* opt_input_img = NULL;
80 const char* opt_output_au = NULL;
81 char* endptr = NULL;
82 int opt_bits = 16;
83 int opt_rate = 48000;
84 int opt_idx = 0;
85 uint8_t total_audio_channels;
86 uint8_t select_audio_channels;
87 uint8_t audio_encoding = SSTVENC_SUNAU_FMT_S16;
88
89 static struct option long_options[] = {
90 {.name = "bits",
91 .has_arg = required_argument,
92 .flag = NULL,
93 .val = 'B'},
94 {.name = "chan",
95 .has_arg = required_argument,
96 .flag = NULL,
97 .val = 'C'},
98 {.name = "mode",
99 .has_arg = required_argument,
100 .flag = NULL,
101 .val = 'M'},
102 {.name = "rate",
103 .has_arg = required_argument,
104 .flag = NULL,
105 .val = 'R'},
106 {.name = "fsk-id",
107 .has_arg = required_argument,
108 .flag = NULL,
109 .val = 'f'},
110 {.name = NULL, .has_arg = 0, .flag = NULL, .val = 0},
111 };
112
113 while (1) {
114 int c = getopt_long(argc, argv, "B:C:M:R:f:", long_options,
115 &opt_idx);
116 if (c == -1) {
117 break;
118 } else if (!c && !long_options[opt_idx].flag) {
119 c = long_options[opt_idx].val;
120 }
121
122 switch (c) {
123 case 0:
124 break;
125 case 'B': /* Set bits */
126 opt_bits = strtol(optarg, &endptr, 10);
127 switch (opt_bits) {
128 case 8:
129 /* 8-bit signed */
130 audio_encoding = SSTVENC_SUNAU_FMT_S8;
131 break;
132 case 16:
133 /* 16-bit signed */
134 audio_encoding = SSTVENC_SUNAU_FMT_S16;
135 break;
136 case 32:
137 switch (endptr[0]) {
138 case 0:
139 case 'S':
140 case 's':
141 /* 32-bit signed */
142 audio_encoding
144 break;
145 case 'f':
146 case 'F':
147 /* 32-bit float */
148 audio_encoding
150 break;
151 }
152 break;
153 case 64:
154 /* 64-bit float */
155 audio_encoding = SSTVENC_SUNAU_FMT_F64;
156 break;
157 default:
158 fprintf(stderr,
159 "Invalid number of bits: %s\n"
160 "Supported values: 8, 16, 32/32s/32f "
161 "and 64\n",
162 optarg);
163 return (1);
164 }
165 break;
166 case 'C': /* Set channel count / usage */
167 opt_channels = optarg;
168 break;
169 case 'M': /* Set SSTV mode */
170 opt_mode = optarg;
171 break;
172 case 'R': /* Set sample rate */
173 opt_rate = atoi(optarg);
174 break;
175 case 'f': /* Set FSK ID */
176 opt_fsk_id = optarg;
177 break;
178 default:
179 show_usage(argv[0]);
180 return 1;
181 }
182 }
183
184 if ((argc - optind) != 2) {
185 show_usage(argv[0]);
186 return 1;
187 }
188 opt_input_img = argv[optind];
189 opt_output_au = argv[optind + 1];
190
191 switch (opt_channels[0]) {
192 case '1':
193 case 'm':
194 case 'M':
195 /* Mono output */
196 total_audio_channels = 1;
197 select_audio_channels = 1;
198 break;
199 case '2':
200 case 's':
201 case 'S':
202 /* Stereo output : both channels */
203 total_audio_channels = 2;
204 select_audio_channels = UINT8_MAX;
205 break;
206 case 'l':
207 case 'L':
208 /* Stereo output : left channel */
209 total_audio_channels = 2;
210 select_audio_channels = 1;
211 break;
212 case 'r':
213 case 'R':
214 /* Stereo output : left channel */
215 total_audio_channels = 2;
216 select_audio_channels = 2;
217 break;
218 default:
219 printf("Unknown channel mode: %s\n", opt_channels);
220 return (1);
221 }
222
223 const struct sstvenc_mode* mode = sstvenc_get_mode_by_name(opt_mode);
224 struct sstvenc_mod mod;
225 struct sstvenc_sunau au;
226
227 if (!mode) {
228 fprintf(stderr, "Unknown mode %s\n", opt_mode);
229 printf("Valid modes are:\n");
230 show_modes();
231 return 1;
232 }
233
234 uint16_t colourspace
236 uint8_t colours = 3;
237 if (colourspace == SSTVENC_CSO_MODE_MONO) {
238 colours = 1;
239 }
240
241 uint8_t* fb
242 = malloc(mode->width * mode->height * colours * sizeof(uint8_t));
243
244 FILE* in = fopen(opt_input_img, "rb");
245 if (!in) {
246 perror("Failed to open input file");
247 return 1;
248 }
249 gdImagePtr im = gdImageCreateFromPng(in);
250 fclose(in);
251
252 /* Resize to destination */
253 gdImagePtr im_resized = gdImageScale(im, mode->width, mode->height);
254
255 for (uint16_t y = 0; y < mode->height; y++) {
256 for (uint16_t x = 0; x < mode->width; x++) {
257 int c = gdImageGetTrueColorPixel(im_resized, x, y);
258 uint32_t idx = sstvenc_get_pixel_posn(mode, x, y);
259 uint8_t pv[colours];
260 uint8_t r = gdTrueColorGetRed(c);
261 uint8_t g = gdTrueColorGetGreen(c);
262 uint8_t b = gdTrueColorGetBlue(c);
263
264 switch (colourspace) {
266 pv[0] = sstvenc_yuv_calc_y(r, g, b);
267 break;
269 pv[0] = r;
270 pv[1] = g;
271 pv[2] = b;
272 break;
275 pv[0] = sstvenc_yuv_calc_y(r, g, b);
276 pv[1] = sstvenc_yuv_calc_u(r, g, b);
277 pv[2] = sstvenc_yuv_calc_v(r, g, b);
278 break;
279 }
280 default:
281 break;
282 }
283
284 for (uint8_t c = 0; c < colours; c++) {
285 fb[idx + c] = pv[c];
286 }
287 }
288 }
289 gdImageDestroy(im_resized);
290 gdImageDestroy(im);
291
292 sstvenc_modulator_init(&mod, mode, opt_fsk_id, fb, 10.0, 10.0,
294 {
295 int res = sstvenc_sunau_enc_init(
296 &au, opt_output_au, mod.osc.sample_rate, audio_encoding,
297 total_audio_channels);
298 if (res < 0) {
299 fprintf(stderr, "Failed to open output file %s: %s\n",
300 opt_output_au, strerror(-res));
301 }
302 }
303
304 /*
305 * Begin writing and computing the audio data.
306 */
307 while (mod.enc.phase != SSTVENC_ENCODER_PHASE_DONE) {
308 /*
309 * Our audio samples for this audio frame
310 */
311 double samples[total_audio_channels];
312
314
315 /* Fill the audio frame */
316 for (uint8_t ch = 0; ch < total_audio_channels; ch++) {
317 if (select_audio_channels & (1 << ch)) {
318 samples[ch] = mod.osc.output;
319 } else {
320 samples[ch] = 0.0;
321 }
322 }
323
324 /* Write the frame out */
325 int au_res = sstvenc_sunau_enc_write(
326 &au, total_audio_channels, samples);
327 if (au_res < 0) {
328 fprintf(stderr, "Failed to write audio samples: %s\n",
329 strerror(-au_res));
330 return (2);
331 }
332 }
333
334 {
335 int au_res = sstvenc_sunau_enc_close(&au);
336 if (au_res < 0) {
337 fprintf(stderr,
338 "Failed to close audio output file: %s\n",
339 strerror(-au_res));
340 return (2);
341 }
342 }
343
344 return 0;
345}
uint32_t sample_rate
Definition oscillator.h:55
#define SSTVENC_CSO_MODE_YUV
Definition sstvmode.h:66
#define SSTVENC_CSO_MODE_RGB
Definition sstvmode.h:63
#define SSTVENC_CSO_MODE_MONO
Definition sstvmode.h:60
#define SSTVENC_CSO_MODE_YUV2
Definition sstvmode.h:75
#define SSTVENC_CSO_MASK_MODE
Definition sstvmode.h:45
struct sstvenc_oscillator osc
Definition sstvmod.h:42
struct sstvenc_encoder enc
Definition sstvmod.h:40
void sstvenc_modulator_init(struct sstvenc_mod *const mod, const struct sstvenc_mode *mode, const char *fsk_id, const uint8_t *framebuffer, double rise_time, double fall_time, uint32_t sample_rate, uint8_t time_unit)
Definition sstvmod.c:14
void sstvenc_modulator_compute(struct sstvenc_mod *const mod)
Definition sstvmod.c:125
#define SSTVENC_ENCODER_PHASE_DONE
Definition sstv.h:80
uint8_t sstvenc_yuv_calc_v(uint8_t r, uint8_t g, uint8_t b)
Definition yuv.c:30
uint8_t sstvenc_yuv_calc_y(uint8_t r, uint8_t g, uint8_t b)
Definition yuv.c:14
uint8_t sstvenc_yuv_calc_u(uint8_t r, uint8_t g, uint8_t b)
Definition yuv.c:22
uint8_t phase
Definition sstv.h:180
uint16_t width
Definition sstvmode.h:215
const char * description
Definition sstvmode.h:157
const char * name
Definition sstvmode.h:160
uint16_t colour_space_order
Definition sstvmode.h:226
uint16_t height
Definition sstvmode.h:220
const struct sstvenc_mode * sstvenc_get_mode_by_idx(uint8_t idx)
Definition sstvmode.c:585
uint64_t sstvenc_mode_get_txtime(const struct sstvenc_mode *const mode, const char *fsk_id)
Definition sstvmode.c:614
const struct sstvenc_mode * sstvenc_get_mode_by_name(const char *name)
Definition sstvmode.c:593
uint32_t sstvenc_get_pixel_posn(const struct sstvenc_mode *const mode, uint16_t x, uint16_t y)
Definition sstvmode.c:704
#define SSTVENC_SUNAU_FMT_F64
Definition sunau.h:35
#define SSTVENC_SUNAU_FMT_F32
Definition sunau.h:34
#define SSTVENC_SUNAU_FMT_S16
Definition sunau.h:32
#define SSTVENC_SUNAU_FMT_S32
Definition sunau.h:33
#define SSTVENC_SUNAU_FMT_S8
Definition sunau.h:31
int sstvenc_sunau_enc_write(struct sstvenc_sunau *const enc, size_t n_samples, const double *samples)
Definition sunau.c:310
int sstvenc_sunau_enc_init(struct sstvenc_sunau *const enc, const char *path, uint32_t sample_rate, uint8_t encoding, uint8_t channels)
Definition sunau.c:288
int sstvenc_sunau_enc_close(struct sstvenc_sunau *const enc)
Definition sunau.c:340
#define SSTVENC_TS_UNIT_MILLISECONDS
Definition timescale.h:37
int main(int argc, char *argv[])
Definition png-to-sstv.c:75
static void show_modes(void)
Definition png-to-sstv.c:14
static void show_usage(const char *prog_name)
Definition png-to-sstv.c:48