code:sstvmod
SSTV code by Fabian Kurz, DJ1YFK - improved speed version by Steve Randall
/* * sstvtx - Martin 1 mode SSTV generator * * $Id: sstvtx.c 82 2008-07-30 12:13:59Z dj1yfk $ * * Copyright (c) 2008 Fabian Kurz, DJ1YFK * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 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. */ /* * Hacked by Steve Randall to speed audio file generation up (and bug fix) * Compile with: gcc sstvtxx.c -lsndfile -ljpeg -lrt -lm -o sstvtx */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <sndfile.h> #include "jpeglib.h" #include "jerror.h" #include <setjmp.h> #include <unistd.h> #include <time.h> // timing profile debug struct timespec tp[8]; // timing profile debug int clock_gettime(clockid_t clk_id, struct timespec *tp); #define PIXEL 1 #define SYNC 0 #define SYNC_FREQ 1200.0 #define BLACK_FREQ 1500.0 #define WHITE_FREQ 2300.0 #define OUT_LEVEL 25000.0 // peak sound level #define TWO_PI 6.28318530717958647693 #define SAMPLE_RATE 48000 // sound file sanples per sec #define PIX_WIDTH 320 #define PIX_HEIGHT 256 /* JPEG input image will be read into this array; picture size 320x256, * RGB components in separate rows */ static unsigned char pic[PIX_WIDTH][PIX_HEIGHT * 3]; typedef struct { int samplerate; double sync_len; // length of synch impulse in sec double pix_len; // length of normal pixel in sec int x_width; double end_phase; // ending phase of pixel double next_time; // time (in sec) to next sample char *infile; char *outfile; } SSTV; void die(char *err); int read_JPEG_file (char *filename); int tx_sync(SNDFILE *snd, SSTV *sstv); int tx_pixel(unsigned char p, SNDFILE *snd, SSTV *sstv, int typ); void help (void); long get_ms(int os) { long ta; long tb; ta = tp[os].tv_sec * 1000l; // convert secs to ms; ta += (tp[os].tv_nsec) / 1000000l; // add in usec converted to ms os++; tb = tp[os].tv_sec * 1000l; // convert secs to ms; tb += (tp[os].tv_nsec) / 1000000l; // add in usec converted to ms return(tb - ta); } void dump_times(void) { fprintf(stderr,"time 0 to 1 %ld ms\n",get_ms(0)); fprintf(stderr,"time 1 to 2 %ld ms\n",get_ms(1)); fprintf(stderr,"time 2 to 3 %ld ms\n",get_ms(2)); } int main (int argc, char **argv) { int x,y; SSTV sstv; SNDFILE *out; SF_INFO out_info; sstv.samplerate = SAMPLE_RATE; sstv.x_width = PIX_WIDTH; /* total pixels = 320 * 256 * 3 = 245760 total sync = 256 * 5ms = 1.28 sec pixel length, in samples, total pixels 245760, 114s - 1.28s sync results in 0.45865885... ms / pixel */ sstv.pix_len = 0.00045865885416666666; sstv.sync_len = 0.005; // sync pulse length 5ms sstv.end_phase = 0.0; sstv.next_time = 1.0 / (double)SAMPLE_RATE; sstv.outfile = "out.wav"; sstv.infile = "input.jpg"; clock_gettime(CLOCK_REALTIME, &tp[0]); while ((x = getopt(argc, argv, "s:i:o:x:h")) != -1) { switch (x) { case 's': sstv.samplerate = atoi(optarg); break; case 'i': sstv.infile = optarg; break; case 'o': sstv.outfile = optarg; break; case 'h': help(); break; case 'x': sstv.x_width= atoi(optarg); break; } } out_info.samplerate = sstv.samplerate; out_info.channels = 1; out_info.format = SF_FORMAT_WAV|SF_FORMAT_PCM_16; if ((out = sf_open(sstv.outfile, SFM_WRITE, &out_info)) == NULL) { die("Opening soundfile failed."); } clock_gettime(CLOCK_REALTIME, &tp[1]); read_JPEG_file(sstv.infile); clock_gettime(CLOCK_REALTIME, &tp[2]); for (y=0; y < (PIX_HEIGHT * 3); y++) { for (x = 0; x < sstv.x_width; x++) { // generate the wav file tx_pixel(pic[x][y], out, &sstv, PIXEL); } if (!(y % 3)) { // insert sync pulse after line generated tx_pixel(0, out, &sstv, SYNC); } } clock_gettime(CLOCK_REALTIME, &tp[3]); sf_close(out); dump_times(); return 0; } void die(char *err) { fprintf(stderr, "Error: %s\n", err); exit(EXIT_FAILURE); } int read_JPEG_file (char *filename) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE *infile; JSAMPARRAY buffer; int row_stride; int i, row=0; if ((infile = fopen(filename, "rb")) == NULL) { die("Can't open input file."); } cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); (void) jpeg_read_header(&cinfo, TRUE); (void) jpeg_start_decompress(&cinfo); if (cinfo.output_width != PIX_WIDTH || cinfo.output_height != PIX_HEIGHT) { die ("Input file format wrong. Need 320x256."); } row_stride = cinfo.output_width * cinfo.output_components; buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); while (cinfo.output_scanline < cinfo.output_height) { (void) jpeg_read_scanlines(&cinfo, buffer, 1); /* each line in buffer: R,G,B,R,G,B -> put to array RRR/GGG/BBB */ for (i=0; i < cinfo.output_width; i++) { pic[i][row] = buffer[0][3*i]; pic[i][row+1] = buffer[0][3*i+1]; pic[i][row+2] = buffer[0][3*i+2]; } row+=3; } (void) jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return 0; } // this routines generates the sound samples representing a single pixel // p contains the pixel intensity int tx_pixel(unsigned char p, SNDFILE *snd, SSTV *sstv, int typ) { double freq; // claculated frequency of sample (in rads/sec) short int buf[500]; // buffer for audio samples int j; // count of samples created double len; // duration of sample to be created double end_phase; // phase at sample end double ph_inc; // phase increment (between audio samples) double phase; // current phase // claculate the frequency associated with the pixel // normalize char to frequency range of 1500-2300Hz if (typ == PIXEL) { freq = TWO_PI * (BLACK_FREQ + (((WHITE_FREQ - BLACK_FREQ) * (double)p)/255.0)); len = sstv->pix_len; } else { freq = TWO_PI * SYNC_FREQ; len = sstv->sync_len; } // calculate the phase at the end of the pixel/sync end_phase = sstv->end_phase + (freq * len); // calculate phase increment between audio samples ph_inc = (freq / sstv->samplerate); // calculate phase at initial sample in pixel/sync phase = sstv->end_phase + (freq) * sstv->next_time; j = 0; while (phase < end_phase) { buf[j++] = (short int) (OUT_LEVEL * sin(phase)); phase += ph_inc; } // claculate time next sample is beyond pixel/sync end sstv->next_time = (phase - end_phase) / (freq); // loop exits with j containing the number of samples written while (end_phase > TWO_PI) { // phase can increment several cycles during sync end_phase -= TWO_PI; // keep in reasonable bounds } sstv->end_phase = end_phase; // new end_phase if (sf_write_short(snd, buf, j) != j) { die("Writing Pixel failed."); } return 0; } void help (void) { printf("sstvtx - generates a SSTV Martin 1 image\n"); printf("\n"); printf("Usage: sstvtx -i input.jpg -o output.wav -x x_width -s samplerate\n"); printf("\n"); printf("Written by Fabian Kurz, DJ1YFK; http://fkurz.net/ham/stuff.html#sstvtx\n\n"); exit(EXIT_SUCCESS); }
code/sstvmod.txt · Last modified: 2008/08/22 21:31 by rocketboy