diff --git a/.gitignore b/.gitignore index 2105ea9..5f550d5 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,10 @@ dkms.conf # debug information files *.dwo +## our application (binary) + +sstv +e36 # special diff --git a/Dayton Paper.pdf b/Dayton Paper.pdf new file mode 100644 index 0000000..054154a Binary files /dev/null and b/Dayton Paper.pdf differ diff --git a/Makefile b/Makefile index cfb056f..f0f271e 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,8 @@ CC = gcc CFLAGS = -Wall -O2 LDFLAGS = -lm -TARGET = sstv -SRCS = main.c sstv.c robot36.c image.c wav.c martin.c +TARGET = e36 +SRCS = main.c sstv.c robot36.c image.c wav.c martin.c scottie.c OBJS = $(SRCS:.c=.o) $(TARGET): $(OBJS) diff --git a/image.c b/image.c index bc723e4..ad5c3c4 100644 --- a/image.c +++ b/image.c @@ -36,6 +36,6 @@ void image_free(image_t *img) { void image_free_raw(image_t *img) { if (img->data) - free(img->data); + free(img->data); // if you do this call only once, cause of double frees img->data = NULL; } diff --git a/main.c b/main.c index 226f581..9779d33 100644 --- a/main.c +++ b/main.c @@ -1,44 +1,69 @@ #include "sstv.h" #include "martin.h" +#include "scottie.h" #include #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"); + fprintf(stderr, + "Usage: %s \n" + "\n" + "Modes:\n" + " robot36 Robot 36 (320x240)\n" + " martin1 Martin M1 (320x256)\n" + " martin2 Martin M2 (320x256)\n" + " scottie1 Scottie S1 (320x256)\n" + " scottie2 Scottie S2 (320x256)\n" + " scottiedx Scottie DX (320x256)\n" + "\n" + "Example:\n" + " %s mode photo.jpg output.wav\n", + prog, prog); } -int main(int argc, char **argv) { - if (argc < 4) { +int main(int argc, char *argv[]) { + if (argc != 4) { usage(argv[0]); return 1; } - const char *mode_str = argv[1]; + const char *mode = argv[1]; const char *input_img = argv[2]; const char *output_wav = argv[3]; + + clock_t start = clock(); + int ok = 0; - if (strcmp(mode_str, "robot36") == 0) { + if (strcmp(mode, "robot36") == 0) { ok = sstv_encode_robot36(input_img, output_wav); - } else if (strcmp(mode_str, "martin1") == 0) { + } else if (strcmp(mode, "martin1") == 0) { ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M1); - } else if (strcmp(mode_str, "martin2") == 0) { + } else if (strcmp(mode, "martin2") == 0) { ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M2); + } else if (strcmp(mode, "scottie1") == 0) { + ok = sstv_encode_scottie(input_img, output_wav, &SCOTTIE_S1); + } else if (strcmp(mode, "scottie2") == 0) { + ok = sstv_encode_scottie(input_img, output_wav, &SCOTTIE_S2); + } else if (strcmp(mode, "scottiedx") == 0) { + ok = sstv_encode_scottie(input_img, output_wav, &SCOTTIE_DX); } else { - fprintf(stderr, "Unknown mode: %s\n", mode_str); + fprintf(stderr, "unknown mode: %s\n\n", mode); usage(argv[0]); return 1; } if (!ok) { - fprintf(stderr, "Encoding failed.\n"); + fprintf(stderr, "make sure the file exsists"); return 1; } - printf("Done.\n"); + clock_t end = clock(); + + double elapsed = (double)(end - start) / CLOCKS_PER_SEC; + + printf("encoded '%s' -> '%s' using '%s'\n", input_img, output_wav, mode); + printf("took %.3f seconds\n", elapsed); return 0; } diff --git a/martin.h b/martin.h index 7ec292c..42ac96b 100644 --- a/martin.h +++ b/martin.h @@ -5,7 +5,7 @@ #include typedef struct { - uint8_t vis_code; + uint8_t vis_code; // in hexadecimal not normal decimal (ex 0x08) int width; int height; double sync_ms; diff --git a/readme.md b/readme.md index ce3b4e3..27ba562 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,79 @@ # sstv e36 encoder -This is a simple C SSTV Robot36 encoder! -Written in pure C +This is a SSTV encoder, currently it is in work, but it has some modes already: + +|Support?|Mode | +|--------|------------| +|[x] |Martin 1 | +|[x] |Martin 2 | +|[x] |Robot 36 | +|[x] |Scottie 1 | +|[x] |Scottie 2 | +|[x] |ScottieD(S)X| + + +Written in _pure_ **C** ## License: This project is distributed under the MiT License! +### For developing or adding to the source code: + +_**This is just standard C17 Standard**_ + +Compilation is done via the gcc compiler, we do NOT allow MSVC here. +If you are on Windows then i suggest to use the WSL. + +Be sure to follow this style of code, it is not that complex, +and some parts you can even tell Vim or Emacs or whatever you use to automate it, +like the header definitions (default i think on vs and some others). + +#### Headers: + +Try to follow this general pattern. +In of itself it is relativley easy to maintain this structure. +And it is also relativley easy to write in this style! + +_**This is just standard C17 Standard**_ +```h +#ifndef HEADER_H +#define HEADER_H + +#include + +typedef struct { + uint8_t type; +} mystruct_t; + +int method_to_use(mystruct_t type); + +#endif // !HEADER_H +``` + +#### C Files: + +For a real implementation this stub below would make 0 sense, +but this is the general style you should follow, it includes CAPITAL definitions, +local imports in "" and at the top, others below. + +Try to comment your code if it does not make sense. +If you encounter something like `Q_rsqrt` (The Quake inverse square root), then you are +permitted to comment some swear words. + +Great example of commenting : "robot36.c" + +```c +#include "header.h" + +#define PI 3 +#define SOME_DEF 67 + +int method_to_use(mystruct_t type) { + return 67 * type; +} +``` + ### Our License: #### MIT : ``` @@ -36,3 +103,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` + diff --git a/scottie.c b/scottie.c new file mode 100644 index 0000000..4f01cb2 --- /dev/null +++ b/scottie.c @@ -0,0 +1,90 @@ +#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; +} diff --git a/scottie.h b/scottie.h new file mode 100644 index 0000000..16e6661 --- /dev/null +++ b/scottie.h @@ -0,0 +1,24 @@ +#ifndef SCOTTIE_H +#define SCOTTIE_H + +#include "wav.h" +#include + +typedef struct { + uint8_t vis_code; + int width; + int height; + double separator_ms; + double sync_ms; + double sync_porch_ms; + double scan_ms; +} scottie_mode_t; + +extern const scottie_mode_t SCOTTIE_S1; +extern const scottie_mode_t SCOTTIE_S2; +extern const scottie_mode_t SCOTTIE_DX; + +void scottie_encode_image(wav_t *wav, uint8_t *rgb, const scottie_mode_t *mode); +int sstv_encode_scottie(const char *input_image, const char *output_wav, const scottie_mode_t *mode); + +#endif // !SCOTTIE_H