/* PiFmAdv - Advanced FM transmitter for the Raspberry Pi Copyright (C) 2017 Miegl See https://github.com/Miegl/PiFmAdv */ #include #include #include #include #include "rds.h" #define PI 3.14159265359 #define FIR_HALF_SIZE 30 #define FIR_SIZE (2*FIR_HALF_SIZE-1) size_t length; // coefficients of the low-pass FIR filter double low_pass_fir[FIR_HALF_SIZE]; double carrier_38[] = {0.0, 0.8660254037844386, 0.8660254037844388, 1.2246467991473532e-16, -0.8660254037844384, -0.8660254037844386}; double carrier_19[] = {0.0, 0.5, 0.8660254037844386, 1.0, 0.8660254037844388, 0.5, 1.2246467991473532e-16, -0.5, -0.8660254037844384, -1.0, -0.8660254037844386, -0.5}; int phase_38 = 0; int phase_19 = 0; double downsample_factor; double *audio_buffer; int audio_index = 0; int audio_len = 0; double audio_pos; double fir_buffer_mono[FIR_SIZE] = {0}; double fir_buffer_stereo[FIR_SIZE] = {0}; int fir_index = 0; int channels; double *last_buffer_val; double preemphasis_prewarp; double preemphasis_coefficient; SNDFILE *inf; double *alloc_empty_buffer(size_t length) { double *p = malloc(length * sizeof(double)); if(p == NULL) return NULL; bzero(p, length * sizeof(double)); return p; } int fm_mpx_open(char *filename, size_t len, int cutoff_freq, int preemphasis_corner_freq, int srate, int nochan) { length = len; if(filename != NULL) { // Open the input file SF_INFO sfinfo; if(filename[0] == '-' && srate > 0 && nochan > 0) { printf("Using stdin for raw audio input at %d Hz.\n",srate); sfinfo.samplerate = srate; sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE; sfinfo.channels = nochan; sfinfo.frames = 0; } // stdin or file on the filesystem? if(filename[0] == '-') { if(!(inf = sf_open_fd(fileno(stdin), SFM_READ, &sfinfo, 0))) { fprintf(stderr, "Error: could not open stdin for audio input.\n"); return -1; } else { printf("Using stdin for audio input.\n"); } } else { if(!(inf = sf_open(filename, SFM_READ, &sfinfo))) { fprintf(stderr, "Error: could not open input file %s.\n", filename); return -1; } else { printf("Using audio file: %s\n", filename); } } int in_samplerate = sfinfo.samplerate; downsample_factor = 228000. / in_samplerate; printf("Input: %d Hz, upsampling factor: %.2f\n", in_samplerate, downsample_factor); channels = sfinfo.channels; if(channels > 1) { printf("%d channels, generating stereo multiplex.\n", channels); } else { printf("1 channel, monophonic operation.\n"); } // Create the preemphasis last_buffer_val = (double*) malloc(sizeof(double)*channels); for(int i=0; i= downsample_factor) { audio_pos -= downsample_factor; if(audio_len <= channels) { for(int j=0; j<2; j++) { // one retry audio_len = sf_read_double(inf, audio_buffer, length); if (audio_len < 0) { fprintf(stderr, "Error reading audio\n"); return -1; } if(audio_len == 0) { if( sf_seek(inf, 0, SEEK_SET) < 0 ) { if(wait) { return 0; } else { fprintf(stderr, "Could not rewind in audio file, terminating\n"); return -1; } } } else { //apply preemphasis int k; int l; double tmp; for(k=0; k= FIR_SIZE) fir_index = 0; // Now apply the FIR low-pass filter /* As the FIR filter is symmetric, we do not multiply all the coefficients independently, but two-by-two, thus reducing the total number of multiplications by a factor of two */ double out_mono = 0; double out_stereo = 0; int ifbi = fir_index; // ifbi = increasing FIR Buffer Index int dfbi = fir_index; // dfbi = decreasing FIR Buffer Index for(int fi=0; fi 1) { out_stereo += low_pass_fir[fi] * (fir_buffer_stereo[ifbi] + fir_buffer_stereo[dfbi]); } ifbi++; if(ifbi >= FIR_SIZE) ifbi = 0; } // End of FIR filter if (channels>1) { mpx_buffer[i] = ((mpx-2)/2) * out_mono + ((mpx-2)/2) * carrier_38[phase_38] * out_stereo + 1 * carrier_19[phase_19]; if (rds) { mpx_buffer[i] += rds_buffer[i]; } phase_19++; phase_38++; if(phase_19 >= 12) phase_19 = 0; if(phase_38 >= 6) phase_38 = 0; } else { mpx_buffer[i] = (mpx-1) * out_mono; if (rds) { mpx_buffer[i] += rds_buffer[i]; } } audio_pos++; } return 0; } int fm_mpx_close() { if(sf_close(inf) ) { fprintf(stderr, "Error closing audio file"); } if(audio_buffer != NULL) free(audio_buffer); return 0; }