/*
    Copyright 2008-2012 Luigi Auriemma

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

    http://www.gnu.org/licenses/gpl-2.0.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/stat.h>
#include <ctype.h>
#include "mywav.h"
//#include "resample2.c"

#ifdef WIN32
#else
    #define stricmp strcasecmp
    #define strnicmp strncasecmp
#endif



typedef int8_t      i8;
typedef uint8_t     u8;
typedef int16_t     i16;
typedef uint16_t    u16;
typedef int32_t     i32;
typedef uint32_t    u32;



#define VER         "0.2"

enum {
    DOT = 1,
    DASH,
    GAP,
    SHORTGAP,
    MEDIUMGAP
};



int morse_output(u8 *beep);
u8 *text2beep(FILE *fd, int *ret_beeps);
int beep2morse(u8 *beep, int beeps, u8 *out, int morseout);
u8 *do_beeps(i16 *smp, int samples, int *ret_beeps);
int mywav_fri24(FILE *fd, uint32_t *num);
i16 *do_samples(FILE *fd, int wavsize, int *ret_samples, int bits);
int do_mono(i16 *smp, int samples, int ch);
void do_dcbias(i16 *smp, int samples);
void do_normalize(i16 *smp, int samples);
int do_reduce(i16 *smp, int samples, int *freq);
void my_err(u8 *err);
void std_err(void);



int     debug               = 0,
        do_abbreviations    = 0,
        do_qcodes           = 0;



int main(int argc, char *argv[]) {
    mywav_fmtchunk  fmt;
    struct  stat    xstat;
    FILE    *fd;
    int     i,
            wavsize,
            samples,
            beeps       = -1,
            raw         = 0,
            optimize    = 1,
            morseout    = 0;
    i16     *smp;
    u8      *beep,
            *beep_out,
            *fname,
            *outfile = NULL;

    setbuf(stdin,  NULL);
    setbuf(stdout, NULL);

    fputs("\n"
        "MORSE2ASCII " VER "\n"
        "by Luigi Auriemma\n"
        "e-mail: aluigi@autistici.org\n"
        "web:    aluigi.org\n"
        "\n", stderr);

    if(argc < 2) {
        printf("\n"
            "Usage: %s [options] <file.WAV/TXT>\n"
            "\n"
            "Options:\n"
            "-a        abbreviations and prosigns parsing\n"
            "-q        q-codes parsing\n"
            "\n"
            "-r F C B  consider the file as raw headerless PCM data, you must specify the\n"
            "          Frequency, Channels and Bits like -r 44100 2 16\n"
            "-o        disable the automatic optimizations: DC bias adjust and normalize.\n"
            "          use this option only if your file is already clean and normalized\n"
            "-m        morse notation output (like ...___... instead of SOS), debug\n"
            "-w FILE   debug option for dumping the handled samples from the memory to FILE\n"
            "-d        debug info (WAV input only)\n"
            "\n"
            "Note: the input file can be a PCM WAV audio file or also a text file which uses\n"
            "      the dotlinespace notation (._ or .-), dit-dah, binary and others\n"
            "      use - as file for reading a text stream from stdin\n"
            "\n", argv[0]);
        exit(1);
    }

    argc--;
    for(i = 1; i < argc; i++) {
        if(((argv[i][0] != '-') && (argv[i][0] != '/')) || (strlen(argv[i]) != 2)) {
            fprintf(stderr, "\nError: wrong argument (%s)\n", argv[i]);
            exit(1);
        }
        switch(argv[i][1]) {
            case 'a': do_abbreviations = 1; break;
            case 'q': do_qcodes = 1;        break;
            case 'r': {
                memset(&fmt, 0, sizeof(fmt));
                if(!argv[++i]) exit(1); fmt.dwSamplesPerSec = atoi(argv[i]);
                if(!argv[++i]) exit(1); fmt.wChannels       = atoi(argv[i]);
                if(!argv[++i]) exit(1); fmt.wBitsPerSample  = atoi(argv[i]);
                fmt.wFormatTag      = 1;
                raw = 1;
                break;
            }
            case 'w': {
                if(!argv[++i]) exit(1); outfile = argv[i];
                break;
            }
            case 'o': optimize  = 0;        break;
            case 'm': morseout  = 1;        break;
            case 'd': debug     = 1;        break;
            default: {
                fprintf(stderr, "\nError: wrong option (%s)\n", argv[i]);
                exit(1);
            }
        }
    }

    fname = argv[argc];

    if(!strcmp(fname, "-")) {
        fprintf(stderr, "- open stdin\n");
        fd = stdin;
    } else {
        fprintf(stderr, "- open %s\n", fname);
        fd = fopen(fname, "rb");
        if(!fd) std_err();
    }

    // stdin or txt
    if((fd == stdin) || ((strlen(fname) > 4) && !stricmp(fname + strlen(fname) - 4, ".txt"))) {
        beep = text2beep(fd, &beeps);
        goto goto_beep2morse;
    }

    if(raw) {
        fstat(fileno(fd), &xstat);
        wavsize = xstat.st_size;
    } else {
        wavsize = mywav_data(fd, &fmt);
    }
    fprintf(stderr,
        "  wave size      %u\n"
        "  format tag     %hu\n"
        "  channels:      %hu\n"
        "  samples/sec:   %u\n"
        "  avg/bytes/sec: %u\n"
        "  block align:   %hu\n"
        "  bits:          %hu\n",
        wavsize,
        fmt.wFormatTag,
        fmt.wChannels,
        fmt.dwSamplesPerSec,
        fmt.dwAvgBytesPerSec,
        fmt.wBlockAlign,
        fmt.wBitsPerSample);

    if(wavsize <= 0) my_err("corrupted WAVE file");
    if(fmt.wFormatTag != 1) my_err("only the classical PCM WAVE files are supported");

    smp = do_samples(fd, wavsize, &samples, fmt.wBitsPerSample);
    fprintf(stderr, "  samples:       %d\n", samples);
    if(fd != stdin) fclose(fd);

    samples = do_mono(smp, samples, fmt.wChannels);
    if(optimize) {
        do_dcbias(smp, samples);
        do_normalize(smp, samples);
    }
    samples = do_reduce(smp, samples, &fmt.dwSamplesPerSec);

    fmt.wFormatTag       = 0x0001;
    fmt.wChannels        = 1;
    fmt.wBitsPerSample   = 16;
    fmt.wBlockAlign      = (fmt.wBitsPerSample >> 3) * fmt.wChannels;
    fmt.dwAvgBytesPerSec = fmt.dwSamplesPerSec * fmt.wBlockAlign;
    wavsize              = samples * sizeof(* smp);

    if(outfile) {
        fprintf(stderr, "- dump %s\n", outfile);
        fd = fopen(outfile, "wb");
        if(!fd) std_err();
        mywav_writehead(fd, &fmt, wavsize, NULL, 0);
        fwrite(smp, 1, wavsize, fd);
        fclose(fd);
    }

    beep = do_beeps(smp, samples, &beeps);

goto_beep2morse:
    if(beeps < 0) {
        for(beeps = 0; beep[beeps]; beeps++);
    }
    beep_out = malloc((beeps * 3) + 1); // morseout visualization and other possible mistakes

    beeps = beep2morse(beep, beeps, beep_out, morseout);

    free(beep);

    fprintf(stderr, "\n- decoded morse data:\n");
    if(morseout) {
        fwrite(beep_out, 1, beeps, stdout);
    } else {
        morse_output(beep_out /*, beeps*/);
    }
    fprintf(stderr, "\n");
    free(beep_out);
    return(0);
}



// http://www.portland-amateur-radio-club.org.uk/resources/q-codes.pdf

#include "abbreviations.h"
#include "qcodes.h"

int morse_output(u8 *beep) {
    int     i,
            ok,
            qa = 0;
    u8      old,
            *p,
            *l,
            *s,
            *z,
            *x;

    p = beep;
    while(*p) {
        if(*p <= ' ') {
            fputc(*p, stdout);
            p++;
            continue;
        }
        for(l = p; *l; l++) {
            //if(!isalnum(*l)) break;
            if((*l >= 0x20) && (*l <= 0x2f)) break;
            if((*l >= 0x3a) && (*l <= 0x40)) break;
            if((*l >= 0x5b) && (*l <= 0x60)) break;
            if((*l >= 0x7b) && (*l <= 0x7e)) break;
            if(*l <= ' ') break;
        }

        ok = 0;
        old = *l;
        *l = 0;

        if(do_abbreviations && !ok) {
            for(i = 0; abbreviations[i].code; i++) {
                if(!stricmp(p, abbreviations[i].code)) {
                    if(abbreviations[i].str) fputs(abbreviations[i].str, stdout);
                    else                     fputs(p, stdout);
                    ok = 1;
                    break;
                }
            }
        }

        if(do_qcodes && !ok) {
            for(i = 0; qcodes[i].code; i++) {
                // isn't possible to have the question but not the answer
                if(qcodes[i].question && !qcodes[i].answer) {
                    printf("\nError: the qcode table used in this tool is invalid (%d %s), contact me", i, qcodes[i].code);
                    exit(1);
                }
                if(!stricmp(p, qcodes[i].code)) {
                    if(!(qa & 1)) {
                        s = qcodes[i].question;
                        z = qcodes[i].question_choices;
                        if(!s) {
                            s = qcodes[i].answer;
                            z = qcodes[i].answer_choices;
                            qa = 1;
                        }
                    } else {
                        s = qcodes[i].answer;
                        z = qcodes[i].answer_choices;
                    }
                    if(!s) {
                        qa = 0;
                        continue;
                    }
                    fputc('\n', stdout);
                    x = strchr(s, '%');
                    if(!x) {
                        fputs(s, stdout);
                    } else {
                        fwrite(s, 1, x - s, stdout);
                        if(z) {
                            fprintf(stdout, "[%s]", z);
                        }
                        fputs(x + 1, stdout);
                        if(s[strlen(s) - 1] != '.') fputc('.', stdout);
                    }
                    fputc('\n', stdout);
                    qa++;
                    ok = 1;
                    break;
                }
            }
        }

        if(!ok) {
            fputs(p, stdout);
        }

        *l = old;
        fputc(old, stdout);
        if(p == l) l++;
        p = l;
    }

    return(0);
}



u8 *text2beep(FILE *fd, int *ret_beeps) {
    int     c,
            i,
            zeroes,
            beeps = 0,
            didah = 0;
    u8      *beep = NULL;

    didah = 0;
    i = 0;
    while((c = fgetc(fd)) >= 0) {
        if(i >= beeps) {
            beeps += 1000;
            beep = realloc(beep, beeps + 16);   // 16 in case of more intra-beeps
            if(!beep) std_err();
        }

        // ditdah
        if(didah) {
            if(c == ' ') {
                beep[i++] = SHORTGAP;
                continue;
            }
            if(c == '-') continue;
            if(c == '.') {
                beep[i++] = MEDIUMGAP;
                continue;
            }
        }
        if(tolower(c) == 'd') {
            didah = 1;
            c = fgetc(fd);  if(c < 0) break;
            if(tolower(c) == 'a') {
                c = '-';
            } else if((tolower(c) == 'i') || (tolower(c) == 'o')) {
                c = '.';
            }
        } else if(tolower(c) == 't') {
            // 't' in dit
            continue;
        } else if((tolower(c) == 'h') || (tolower(c) == 's')) {
            // 'h' in dah
            continue;
        }

        // binary
        if(c == '0') {
            for(zeroes = 1;; zeroes++) {
                c = fgetc(fd);  if(c < 0) break;
                if(c == '1') break;
            }
            if(zeroes <= 1) {
            } else if(zeroes <= 3) {
                beep[i++] = SHORTGAP;
            } else if(zeroes > 3) {
                beep[i++] = MEDIUMGAP;
            }
        }
        if(c == '1') {
            c = fgetc(fd);  if(c < 0) c = '0';  /*if(c < 0) break;*/    // work-around
            if(c <= '0') {
                c = '.';
            } else {
                c = fgetc(fd);  if(c < 0) break; // 1
                c = fgetc(fd);  if(c < 0) break; // 1
                c = '-';
            }
        }

        switch(c) {
            case 0x07:  // bell, used on wikipedia
            case '.':
            case 'o':
            case 0xb7:  // '·':
            case 0xb0:
            case 0xf8:
            case '|':
            case '*':
            case 'O':  beep[i++] = DOT;         break;

            case 0xaf:
            case '_':
            case '-':  beep[i++] = DASH;        break;

            case '\t':
            case ' ': {
                if((i > 0) && ((beep[i - 1] == SHORTGAP) || (beep[i - 1] == MEDIUMGAP))) {
                    beep[i - 1] = MEDIUMGAP;
                } else {
                    beep[i++] = SHORTGAP;
                }
                break;
            }
            case '/':  beep[i++] = SHORTGAP;    break;
            case '\r':
            case '\n':
            default: {
                if((i > 0) && (beep[i - 1] == MEDIUMGAP)) {
                    // do nothing
                } else {
                    beep[i++] = MEDIUMGAP;
                }
                break;
            }
        }
    }

    beep[i] = 0;
    if(ret_beeps) *ret_beeps = i;
    return(beep);
}



int beep2morse(u8 *beep, int beeps, u8 *out, int morseout) {
    static const unsigned morse2chr[256] = {    // created with my buildmorsetab tool
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    3,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        3, 2458, 1625,    0, 5526,    0,  405, 1705,  617, 2470,  150,  409, 2650, 2390, 1638,  601,
      682,  426,  362,  346,  342,  341,  597,  661,  677,  681, 2709, 2457,    0,  598,    0, 1445,
     1689,    6,  149,  153,   37,    1,   89,   41,   85,    5,  106,   38,  101,   10,    9,   42,
      105,  166,   25,   21,    2,   22,   86,   26,  150,  154,  165,    0,    0,    0,    0, 1446,
        0,    6,  149,  153,   37,    1,   89,   41,   85,    5,  106,   38,  101,   10,    9,   42,
      105,  166,   25,   21,    2,   22,   86,   26,  150,  154,  165,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,  170,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      422,    0,    0,    0,  102,  422,  102,  613,  406,  357,    0,    0,    0,    0,    0,    0,
      361,  666,    0,    0,    0,    0,  169,    0,  169,    0,    0,    0,   90,    0,  421,    0
    };
    unsigned    chr;
    int     i,
            j;
    u8      *c;

    c = out;
    if(morseout) {
        for(i = 0; i < beeps; i++) {
            switch(beep[i]) {
                case DOT:       *c++ = '.'; break;
                case DASH:      *c++ = '-'; break;
                case SHORTGAP:  *c++ = ' '; break;
                case MEDIUMGAP: {
                    *c++ = ' ';
                    *c++ = '/';
                    *c++ = ' ';
                    break;
                }
                default: break;
            }
        }
    } else {
        for(i = 0; i < beeps; i++) {
            for(chr = 0; i < beeps; i++) {
                if((beep[i] == SHORTGAP) || (beep[i] == MEDIUMGAP)) break;
                if(beep[i] == DOT) {
                    chr <<= 2;
                    chr |= 1;
                } else if(beep[i] == DASH) {
                    chr <<= 2;
                    chr |= 2;
                }
            }

            // some chars have duplicates, for example '*' with 'x'
            // then this method allows me to use lower chars
            // automatically
            for(j = 'a'; j <= 'z'; j++) {
                if(morse2chr[j] == chr) goto beep2morse_found;
            }
            for(j = 0; j < 256; j++) {
                if(morse2chr[j] == chr) goto beep2morse_found;
            }

        beep2morse_found:
            if(chr == 169) j = '!'; // old MN digraph
            if(j <= 0) {
                // do nothing
            } else if(j <= 0xff) {
                *c++ = j;
            } else {
                *c++ = ' ';
            }
            if(beep[i] == MEDIUMGAP) *c++ = ' ';
        }
    }
    *c = 0;
    return(c - out);
}



u8 *do_beeps(i16 *smp, int samples, int *ret_beeps) {
    u32     tmp;
    int     i,
            j,
            beeps,
            min_on,  min_off,  max_on,  max_off,
            tmin_on, tmin_off, tmax_on, tmax_off,
            med_on,  med_off,
            divx = 20,  // freq / divx, in short it's the amount of samples to control to decide if they are 1 or 0
            divx2;      // declared only for an experimental way I was working on
    i16     *s;
    u8      *beep;

    beeps = samples / divx;

    beep = malloc(beeps * sizeof(* beep));
    if(!beep) std_err();

    s = smp;
    for(i = 0; i < beeps; i++) {
        for(tmp = divx2 = j = 0; j < divx; j++, s++) {
            //            if(*s < 0) *s = -(*s);
            if(*s > 0) *s = 0; //continue;
            if(*s < 0) *s = (-*s) - 1;
            tmp += *s;
            divx2++;
        }
        if(!divx2) {
            beep[i] = 0;
        } else {
            beep[i] = ((tmp / divx2) > 3000) ? 1 : 0;
        }
    }

    min_on  = 0x7fffffff;
    min_off = 0x7fffffff;
    max_on  = 0;
    max_off = 0;
    tmin_on = tmin_off = tmax_on = tmax_off = 0;
    for(i = 1; i < beeps; i++) {
        if(beep[i] && beep[i - 1]) {
            tmin_on++;
            tmax_on++;
        } else if(!beep[i] && !beep[i - 1]) {
            tmin_off++;
            tmax_off++;
        } else if(beep[i] && !beep[i - 1]) {
            if(tmin_off < min_off) min_off = tmin_off;
            if(tmax_off > max_off) max_off = tmax_off;
            tmin_on = tmax_on = tmin_off = tmax_off = 0;
        } else if(!beep[i] && beep[i - 1]) {
            if(tmin_on  < min_on)  min_on  = tmin_on;
            if(tmax_on  > max_on)  max_on  = tmax_on;
            tmin_on = tmax_on = tmin_off = tmax_off = 0;
        }
    }
    min_on++;
    min_off++;
    max_on++;
    max_off++;
    med_on  = ((min_on  + max_on)  / 3) * 2;
    if(!med_on) med_on = min_on;
    med_off = ((min_off + max_off) / 3) * 2;
    if(!med_off) med_off = min_off;
    if((max_off / min_off) < 7) {
        max_off++;
    } else {
        med_off = min_off * 2;
        max_off = ((med_off + max_off) / 3) * 2;
    }

    if(debug) {
        fprintf(stderr,
            "  min_on         %d\n"
            "  med_on         %d\n"
            "  max_on         %d\n"
            "  min_off        %d\n"
            "  med_off        %d\n"
            "  max_off        %d\n",
            min_on,  med_on,  max_on,
            min_off, med_off, max_off);
    }

    i = 0;
    j = 0;
    while(i < beeps) {
        if(beep[i]) {
            for(tmp = 0; (i < beeps) && beep[i]; tmp++, i++);
            if(tmp <= med_on)       beep[j++] = DOT;
            else if(tmp > med_on)   beep[j++] = DASH;
        } else {
            for(tmp = 0; (i < beeps) && !beep[i]; tmp++, i++);
            if(tmp <= med_off)      beep[j++] = GAP;
            else if(tmp >= max_off) beep[j++] = MEDIUMGAP;
            else if(tmp > med_off)  beep[j++] = SHORTGAP;
        }
    }
    beeps = j;

    if(debug) {
        for(i = 0; i < beeps; i++) {
            if(!(i % 32)) fprintf(stderr, "\n");
            fprintf(stderr, " %d", beep[i]);
        }
        fprintf(stderr, "\n");
    }

    if(ret_beeps) *ret_beeps = beeps;
    return(beep);
}



int mywav_fri24(FILE *fd, uint32_t *num) {
    uint32_t    ret;
    uint8_t     tmp;

    if(fread(&tmp, 1, 1, fd) != 1) return(-1);  ret = tmp;
    if(fread(&tmp, 1, 1, fd) != 1) return(-1);  ret |= (tmp << 8);
    if(fread(&tmp, 1, 1, fd) != 1) return(-1);  ret |= (tmp << 16);
    *num = ret;
    return(0);
}



i16 *do_samples(FILE *fd, int wavsize, int *ret_samples, int bits) {
    i32     tmp32;
    int     i   = 0,
            samples;
    i16     *smp;
    i8      tmp8;

    samples = wavsize / (bits >> 3);
    smp = malloc(sizeof(* smp) * samples);
    if(!smp) std_err();

    if(bits == 8) {
        for(i = 0; i < samples; i++) {
            if(mywav_fri08(fd, &tmp8) < 0) break;
            smp[i] = (tmp8 << 8) - 32768;
        }

    } else if(bits == 16) {
        for(i = 0; i < samples; i++) {
            if(mywav_fri16(fd, &smp[i]) < 0) break;
        }

    } else if(bits == 24) {
        for(i = 0; i < samples; i++) {
            if(mywav_fri24(fd, &tmp32) < 0) break;
            smp[i] = tmp32 >> 8;
        }

    } else if(bits == 32) {
        for(i = 0; i < samples; i++) {
            if(mywav_fri32(fd, &tmp32) < 0) break;
            smp[i] = tmp32 >> 16;
        }

    } else {
        my_err("number of bits used in the WAVE file not supported");
    }
    *ret_samples = i;
    return(smp);
}



int do_mono(i16 *smp, int samples, int ch) {
    i32     tmp;    // max 65535 channels
    int     i,
            j;

    if(!ch) my_err("the WAVE file doesn't have channels");
    if(ch == 1) return(samples);

    for(i = 0; samples > 0; i++) {
        tmp = 0;
        for(j = 0; j < ch; j++) {
            tmp += smp[(i * ch) + j];
        }
        smp[i] = tmp / ch;
        samples -= ch;
    }
    return(i);
}



void do_dcbias(i16 *smp, int samples) {
    int     i;
    i16     bias,
            maxneg,
            maxpos;

    maxneg = 32767;
    maxpos = -32768;
    for(i = 0; i < samples; i++) {
        if(smp[i] < maxneg) {
            maxneg = smp[i];
        } else if(smp[i] > maxpos) {
            maxpos = smp[i];
        }
    }

    bias = (maxneg + maxpos) / 2;
    fprintf(stderr, "  bias adjust:   %d\n", bias);

    for(i = 0; i < samples; i++) {
        smp[i] -= bias;
    }
}



void do_normalize(i16 *smp, int samples) {
    int     i;
    double  t;
    i16     bias,
            maxneg,
            maxpos;

    maxneg = 0;
    maxpos = 0;
    for(i = 0; i < samples; i++) {
        if(smp[i] < maxneg) {
            maxneg = smp[i];
        } else if(smp[i] > maxpos) {
            maxpos = smp[i];
        }
    }

    fprintf(stderr, "  volume peaks:  %d %d\n", maxneg, maxpos);

    if(maxneg < 0) maxneg = (-maxneg) - 1;
    if(maxneg > maxpos) {
        bias = maxneg;
    } else {
        bias = maxpos;
    }
    if(bias >= 32767) return;

    fprintf(stderr, "  normalize:     %d\n", 32767 - bias);

    for(i = 0; i < samples; i++) {
        t = smp[i];
        t = (t * (double)32767) / (double)bias;
        if(t > (double)32767)  t = 32767;
        if(t < (double)-32768) t = -32768;
        smp[i] = t;
    }
}



void *av_resample_init(int, int, int, int, int, double);
int av_resample(void *, short *, short *, int *, int, int, int);
void av_resample_close(void *);

int do_reduce(i16 *smp, int samples, int *freq) {
    void    *res;
    int     consumed,
            newfreq     = 8000;

    fprintf(stderr, "  resampling to  %dhz\n", newfreq);
    res = av_resample_init(newfreq, *freq, 16, 10, 0, 0.8);
    samples = av_resample(res, smp, smp, &consumed, samples, samples, 1);
    av_resample_close(res);

    *freq = newfreq;
    return(samples);
}



void my_err(u8 *err) {
    fprintf(stderr, "\nError: %s\n", err);
    exit(1);
}



void std_err(void) {
    perror("\nError");
    exit(1);
}


