first pass at embedded freedv API
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 4 Aug 2014 00:21:20 +0000 (00:21 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 4 Aug 2014 00:21:20 +0000 (00:21 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@1783 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/README
codec2-dev/src/CMakeLists.txt
codec2-dev/src/fec_enc.c
codec2-dev/src/freedv.c [deleted file]
codec2-dev/src/freedv_api.c [new file with mode: 0644]
codec2-dev/src/freedv_api.h [new file with mode: 0644]
codec2-dev/src/freedv_rx.c [new file with mode: 0644]
codec2-dev/src/freedv_tx.c [new file with mode: 0644]

index c070222d43e7e98d1919a0671ef9c54b60692ccd..0f236e513c69bd8009dedf52e329a0b85fc5204f 100644 (file)
@@ -6,31 +6,41 @@ and below.  For more information please see:
 
     http://rowetel.com/codec2.html
 
-Also included is a FDMDV modem, see README_fdmdv.txt
+Also included is a FDMDV modem (README_fdmdv.txt), and an API for
+embeddeding FreeDV in other programs (see example below).  For more
+information on building Codec 2 see READE.cmake
 
 Quickstart
 ----------
 
 1/ Listen to Codec 2:
- *** FIRST STEP OBSOLETE: See README.cmake for building ***
-   $ ./configure && make
-   $ cd src
-   $ ./c2demo ../raw/hts1a.raw hts1a_c2.raw
-   $ ../script/menu.sh ../raw/hts1a.raw hts1a_c2.raw
-
-   NOTE: For playback testing, menu.sh requires either the 'play',
-   'aplay' or 'ossplay' programs to be installed (see
-   http://sox.sourceforge.net/, http://www.alsa-project.org/, or
-   http://www.opensound.com/ respectively).
+   $ cd codec2
+   $ mkdir build_linux
+   $ cmake ..
+   $ make
+   $ ./src/c2demo ../raw/hts1a.raw hts1a_c2.raw
+   $ play ../raw/hts1a.raw hts1a_c2.raw
 
 2/ Compress and Decompress a file:
 
-   $ ./c2enc 2400 ../raw/hts1a.raw hts1a_c2.bit
-   $ ./c2dec 2400 hts1a_c2.bit hts1a_c2.raw 
+   $ ./src/c2enc 2400 ../raw/hts1a.raw hts1a_c2.bit
+   $ ./src/c2dec 2400 hts1a_c2.bit hts1a_c2.raw 
 
 3/ Same thing with pipes:
 
-   $ ./c2enc 1400 ../raw/hts1a.raw - | ./c2dec 1400 - - | play -t raw -r 8000 -s -2 -
+   $ ./src/c2enc 1400 ../raw/hts1a.raw - | ./src/c2dec 1400 - - | play -t raw -r 8000 -s -2 -
+
+Embedded FreeDV API
+-------------------
+
+See freedv_api.h and freedv_api.c, and the demo programs freedv_tx &
+freedv_rx.  Quickstart:
+
+  $ ./freedv_tx ../../raw/hts1.raw - | ./freedv_rx - - | play -t raw -r 8000 -s -2 -
+
+Or with a simulated 2 dB SNR channel:
+
+  $ ./freedv_tx ../../raw/hts1.raw - | ./fdmdv_channel - - 2 | ./freedv_rx - - | play -t raw -r 8000 -s -2 
 
 Programs
 --------
@@ -64,21 +74,12 @@ Debugging
 Directories
 -----------
 
-  fltk      - FLTK GUI programs(s)
   octave    - Octave scripts used for visualising internal signals 
               during development
-  portaudio - Portaudio test programs
   script    - shell scripts for playing and converting raw files
   src       - C source code
   raw       - speech files in raw format (16 bits signed linear 8 kHz)
   unittest  - unit test source code
-  voicing   - hand-estimated voicing files, used for development
   wav       - speech files in wave file format
-  win32     - Support for building Windows DLL version of Codec 2 and FDMDV libraries
-
-TODO
-----
+  stm32     - Support for the STM32F4 microcntroller
 
-[ ] Get win32/Makefile integrated into Automake system, such that if
-    i586-mingw32msvc exists the Win32 code gets automatically built.
-[ ] Same for fltk & portaudio, build these conditionally if libs exist
index dca5aa0cf32871fbada0603533cda921bfd699de..eb1d70afe9505e786ae888a4de59bd41c30b864f 100644 (file)
@@ -183,6 +183,7 @@ set(CODEC2_SRCS
     codebookdt.c
     codebookge.c
     golay23.c
+    freedv_api.c
 )
 
 add_library(codec2 ${CODEC2_SRCS})
@@ -226,8 +227,8 @@ target_link_libraries(fdmdv_demod ${CMAKE_REQUIRED_LIBRARIES})
 add_executable(fdmdv_put_test_bits fdmdv_put_test_bits.c fdmdv.c kiss_fft.c)
 target_link_libraries(fdmdv_put_test_bits ${CMAKE_REQUIRED_LIBRARIES})
 
-add_executable(fdmdv_channel fdmdv_channel.c fdmdv.c kiss_fft.c)
-target_link_libraries(fdmdv_channel ${CMAKE_REQUIRED_LIBRARIES})
+add_executable(fdmdv_channel fdmdv_channel.c)
+target_link_libraries(fdmdv_channel ${CMAKE_REQUIRED_LIBRARIES} codec2)
 
 add_executable(fdmdv_interleave fdmdv_interleave.c)
 target_link_libraries(fdmdv_interleave ${CMAKE_REQUIRED_LIBRARIES})
@@ -241,6 +242,13 @@ target_link_libraries(fec_enc ${CMAKE_REQUIRED_LIBRARIES} codec2)
 add_executable(fec_dec fec_dec.c golay23.c)
 target_link_libraries(fec_dec ${CMAKE_REQUIRED_LIBRARIES} codec2)
 
+add_executable(freedv_tx freedv_tx.c)
+target_link_libraries(freedv_tx ${CMAKE_REQUIRED_LIBRARIES} codec2)
+
+add_executable(freedv_rx freedv_rx.c)
+target_link_libraries(freedv_rx ${CMAKE_REQUIRED_LIBRARIES} codec2)
+
+
 install(TARGETS
     codec2
     c2demo
index ad5952ee56b07a30a4f65fddb54405eaec5fb010..a26731fe70fbff912310df5e671220bbb0722257 100644 (file)
@@ -267,6 +267,10 @@ int main(int argc, char *argv[])
                 unpacked_output_bits[i] = (codeword1 >> (10-j)) & 0x1;
             }
             unpacked_output_bits[i] = 0; /* spare bit */
+
+            //for(i=0; i<bits_per_input_frame+12; i++)
+            //    printf("%d\n", unpacked_output_bits[i]);
+
         }
 
         /* pack bits, MSB first  */
diff --git a/codec2-dev/src/freedv.c b/codec2-dev/src/freedv.c
deleted file mode 100644 (file)
index aca990c..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-/*---------------------------------------------------------------------------*\
-                                                                             
-  FILE........: freedv.c
-  AUTHOR......: David Rowe
-  DATE CREATED: August 2014
-                                                                             
-  Functions that implement FreeDV "modes" (or waveforms), useful for
-  embedding in other programs.
-                                                                             
-\*---------------------------------------------------------------------------*/
-
-/*
-  Copyright (C) 2012 David Rowe
-
-  All rights reserved.
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License version 2.1, as
-  published by the Free Software Foundation.  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 Lesser General Public License
-  along with this program; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#define FREEDV_1600       0
-#define FREEDV_NSAMPLES 320
-
-#include "codec2.h"
-#include "codec2_fdmdv.h"
-#include "golay23.h"
-
-
-struct freedv {
-    int           mode;
-    void         *codec2;
-    struct FDMDV *fdmdv
-    char         *packed_codec_bits;
-    int          *codec_bits;
-    int          *tx_bits;
-    int          *fdmdv_bits;
-    int          *rx_bits;
-    int           tx_sync_bit;
-};
-
-
-struct freedv *freedv_open(int mode) {
-    struct freedv *f;
-    int            Nc, codec2_mode, nbit, nbyte;
-
-    f = (struct freedv*)malloc(sizeof(struct freedv));
-    if (f == NULL)
-        return NULL;
-
-    f->mode = mode;
-    f->tx_sync = 0;
-
-    if (mode == FREEDV_1600) {
-        Nc = 16;
-        codec2_mode = CODEC2_MODE_1300;
-    }
-
-    f->codec2 = codec2_create(Nc);
-    if (f->codec2 == NULL)
-        return NULL;
-
-    f->fdmdv = fdmdv_create(codec2_mode);
-    if (f->fdmdv == NULL)
-        return NULL;
-
-    nbit = codec2_bits_per_frame(f->codec2);
-    nbyte = (nbit + 7) / 8;
-    f->packed_codec_bits = (unsigned char*)malloc(nbyte*sizeof(char));
-    f->codec_bits = (int*)malloc(nbit*sizeof(int));
-
-    nbit = 2*fdmdv_bits_per_frame(fdmdv);
-    f->tx_bits = (int*)malloc(nbit*sizeof(int));
-    f->rx_bits = (int*)malloc(nbit*sizeof(int));
-    nbit = fdmdv_bits_per_frame(fdmdv);
-    f->fdmdv_bits = (int*)malloc(nbit*sizeof(int));
-
-    if ((f->packed_codec_bits == NULL) || (f->codec_bits == NULL) 
-        || (f->tx_bits == NULL) || (f->rx_bits == NULL) || (f->fdmdv_bits == NULL))
-        return NULL;
-
-    golay23_init();
-
-    return f;
-}
-
-void freedv_close(struct freedv *freedv) {
-    free(freedv->packed_codec_bits);
-    free(freedv->codec_bits);
-    free(freedv->tx_bits);
-    fdmdv_destroy(freedv->fdmdv);
-    codec2_destroy(freedv->codec2);
-    free(freedv);
-}
-
-int freedv_tx(struct freedv *f, short speech_in[], short mod_out[]) {
-    int    bit, byte, i, j;
-    int    bits_per_codec_frame, bits_per_fdmdv_frame;
-    int    data, codeword1, codeword2;
-    COMP   tx_fdm[2*FDMDV_NOM_SAMPLES_PER_FRAME];
-    short  tx_fdm_scaled[2*FDMDV_NOM_SAMPLES_PER_FRAME];
-     
-    bits_per_codec_frame = codec2_bits_per_frame(freedv->codec2);
-    bits_per_fdmdv_frame = fdmdv_bits_per_frame(freedv->codec2);
-
-    codec2_encode(f->codec2, f->packet_codec_bits, speech_in);
-
-    /* unpack bits, MSB first */
-
-    bit = 7; byte = 0;
-    for(i=0; i<bits_per_input_frame; i++) {
-        f->codec_bits[i] = (f->packed_codec_bits[byte] >> bit) & 0x1;
-        bit--;
-        if (bit < 0) {
-            bit = 7;
-            byte++;
-        }
-    }
-    
-    if (f->mode == MODE_1600) {
-            
-        /* Protect first 12 out of first 16 excitation bits with (23,12) Golay Code:
-
-           0,1,2,3: v[0]..v[1]
-           4,5,6,7: MSB of pitch
-           11,12,13,14: MSB of energy
-
-        */
-
-        data = 0;
-        for(i=0; i<8; i++) {
-            data <<= 1;
-            data |= f->codec_bits[i];
-        }
-        for(i=11; i<15; i++) {
-            data <<= 1;
-            data |= f->codec_bits[i];
-        }
-        codeword1 = golay23_encode(data);
-
-        /* now pack output frame with parity bits at end to make them
-           as far apart as possible from the data they protect.  Parity
-           bits are LSB of the Golay codeword */
-
-        for(i=0; i<bits_per_codec_frame; i++)
-            f->tx_bits[i] = f->codec_bits[i];
-        for(j=0; i<bits_per_codec_frame+11; i++,j++) {
-            f->tx_bits[i] = (codeword1 >> (10-j)) & 0x1;
-        }
-        f->tx_bits[i] = 0; /* spare bit */
-    }
-
-    /* modulate even and odd frames */
-
-    fdmdv_mod(f->fdmdv, tx_fdm, f->tx_bits, &f->tx_sync_bit);
-    assert(sync_bit == 1);
-
-    fdmdv_mod(f->fdmdv, &tx_fdm[FDMDV_NOM_SAMPLES_PER_FRAME], &f->tx_bits[bits_per_fdmdv_frame], &f->tx_sync_bit);
-    assert(sync_bit == 0);
-
-    for(i=0; i<2*FDMDV_NOM_SAMPLES_PER_FRAME; i++)
-        mod_out[i] = FDMDV_SCALE * tx_fdm[i].real;
-}
-
-int freedv_nin(struct freedv *f) {
-    return f->nin;
-}
-
-/* TODO: sync code, SNR threshold (default but can be changed), unit test program */
-
-int freedv_rx(struct freedv *f, short demod_in[], short speech_out[]) {
-    COMP  rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
-    int   bits_per_codec_frame, bytes_per_codec_frame, bits_per_fdmdv_frame;
-    int   reliable_sync_bit, i, j, bit, byte, ;
-    int   recd_codeword, codeword1, codeword2;
-
-    bits_per_codec_frame  = codec2_bits_per_frame(freedv->codec2);
-    bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8;
-    bits_per_fdmdv_frame  = fdmdv_bits_per_frame(freedv->codec2);
-
-    for(i=0; i<f->nin; i++) {
-        rx_fdm[i].real = (float)demod_in[i]/FDMDV_SCALE;
-        rx_fdm[i].imag = 0;
-    }
-    nin_prev = nin;
-    fdmdv_demod(f->fdmdv, f->fdmdv_bits, &reliable_sync_bit, rx_fdm, &f->nin);
-    
-    if (reliable_sync_bit == 0) {
-        memcpy(f->rx_bits, f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int));
-    }
-    else {
-        memcpy(&f->rx_bits[bits_per_fdmdv_frame], f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int));
-    }
-
-    if (f->mode == MODE_1600) {
-        recd_codeword = 0;
-        for(i=0; i<8; i++) {
-            recd_codeword <<= 1;
-            recd_codeword |= f->rx_bits[i];
-        }
-        for(i=11; i<15; i++) {
-            recd_codeword <<= 1;
-            recd_codeword |= f->rx_bits[i];
-        }
-        for(i=bits_per_codec_frame; i<bits_per_codec_frame+11; i++) {
-            recd_codeword <<= 1;
-            recd_codeword |= f->rx_bits[i];
-        }
-        codeword1 = golay23_decode(recd_codeword);
-        //codeword1 = recd_codeword;
-        //fprintf(stderr, "received codeword1: 0x%x  decoded codeword1: 0x%x\n", recd_codeword, codeword1);
-           
-        for(i=0; i<bits_per_codec_frame; i++)
-            f->codec_bits[i] = f->rx_bits[i];
-
-        for(i=0; i<8; i++) {
-            f->codec_bits[i] = (codeword1 >> (22-i)) & 0x1;
-        }
-        for(i=8,j=11; i<12; i++,j++) {
-            f->codec_bits[j] = (codeword1 >> (22-i)) & 0x1;
-        }
-    }
-    // pack bits, MSB received first
-
-    bit  = 7;
-    byte = 0;
-    memset(f->packed_codec_bits, 0,  bytes_per_codec_frame);
-    for(i=0; i<bits_per_codec_frame; i++) {
-        f->packed_codec_bits[byte] |= (f->codec_bits[i] << bit);
-        bit--;
-        if(bit < 0) {
-            bit = 7;
-            byte++;
-        }
-    }
-
-    codec2_decode(f->codec2, speech_out, f->packed_codec_bits);
-}
-
-int freedv_tx_text(struct freedv_mode *mode, char c) {
-}
-
-int freedv_rx_text(struct freedv_mode *mode, char *c) {
-}
-
diff --git a/codec2-dev/src/freedv_api.c b/codec2-dev/src/freedv_api.c
new file mode 100644 (file)
index 0000000..2c62334
--- /dev/null
@@ -0,0 +1,358 @@
+/*---------------------------------------------------------------------------*\
+                                                                             
+  FILE........: freedv_api.c
+  AUTHOR......: David Rowe
+  DATE CREATED: August 2014
+                                                                             
+  Library of API functions that implement FreeDV "modes", useful for
+  embedding FreeDV in other programs.
+      
+  TODO:
+    [ ] speex tx/rx works
+    [ ] txt messages
+    [ ] optional test tx framemode
+                                                                       
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2014 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  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 Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "codec2.h"
+#include "codec2_fdmdv.h"
+#include "golay23.h"
+#include "freedv_api.h"
+
+/*---------------------------------------------------------------------------*\
+                                                       
+  FUNCTION....: freedv_open
+  AUTHOR......: David Rowe                           
+  DATE CREATED: 3 August 2014
+
+  Call this first to initialise.  Returns NULL if initialisation fails
+  (e.g. out of memory or mode not supported).
+
+\*---------------------------------------------------------------------------*/
+
+struct freedv *freedv_open(int mode) {
+    struct freedv *f;
+    int            Nc, codec2_mode, nbit, nbyte;
+
+    if (mode != FREEDV_MODE_1600)
+        return NULL;
+
+    f = (struct freedv*)malloc(sizeof(struct freedv));
+    if (f == NULL)
+        return NULL;
+
+    f->mode = mode;
+    f->tx_sync_bit = 0;
+    f->snr_thresh = 2.0;
+
+    if (mode == FREEDV_MODE_1600) {
+        Nc = 16;
+        codec2_mode = CODEC2_MODE_1300;
+    }
+
+    f->codec2 = codec2_create(codec2_mode);
+    if (f->codec2 == NULL)
+        return NULL;
+
+    f->fdmdv = fdmdv_create(Nc);
+    if (f->fdmdv == NULL)
+        return NULL;
+
+    nbit = codec2_bits_per_frame(f->codec2);
+    nbyte = (nbit + 7) / 8;
+    f->packed_codec_bits = (unsigned char*)malloc(nbyte*sizeof(char));
+    f->codec_bits = (int*)malloc(nbit*sizeof(int));
+
+    nbit = 2*fdmdv_bits_per_frame(f->fdmdv);
+    f->tx_bits = (int*)malloc(nbit*sizeof(int));
+    f->rx_bits = (int*)malloc(nbit*sizeof(int));
+    nbit = fdmdv_bits_per_frame(f->fdmdv);
+    f->fdmdv_bits = (int*)malloc(nbit*sizeof(int));
+
+    if ((f->packed_codec_bits == NULL) || (f->codec_bits == NULL) 
+        || (f->tx_bits == NULL) || (f->rx_bits == NULL) || (f->fdmdv_bits == NULL))
+        return NULL;
+
+    golay23_init();
+
+    return f;
+}
+
+/*---------------------------------------------------------------------------*\
+                                                       
+  FUNCTION....: freedv_close
+  AUTHOR......: David Rowe                           
+  DATE CREATED: 3 August 2014
+
+  Frees up memory.
+
+\*---------------------------------------------------------------------------*/
+
+void freedv_close(struct freedv *freedv) {
+    free(freedv->packed_codec_bits);
+    free(freedv->codec_bits);
+    free(freedv->tx_bits);
+    fdmdv_destroy(freedv->fdmdv);
+    codec2_destroy(freedv->codec2);
+    free(freedv);
+}
+
+/*---------------------------------------------------------------------------*\
+                                                       
+  FUNCTION....: freedv_tx
+  AUTHOR......: David Rowe                           
+  DATE CREATED: 3 August 2014
+
+  Takes a frame of input speech samples, encodes and modulates them to produce
+  a frame of modem samples that can be sent to the transmitter.
+
+  Both speech_in[] and mod_out[] are FREEDV_NSAMPLES long.
+
+\*---------------------------------------------------------------------------*/
+
+void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) {
+    int    bit, byte, i, j;
+    int    bits_per_codec_frame, bits_per_fdmdv_frame;
+    int    data, codeword1;
+    COMP   tx_fdm[2*FDMDV_NOM_SAMPLES_PER_FRAME];
+     
+    bits_per_codec_frame = codec2_bits_per_frame(f->codec2);
+    bits_per_fdmdv_frame = fdmdv_bits_per_frame(f->fdmdv);
+
+    codec2_encode(f->codec2, f->packed_codec_bits, speech_in);
+
+    /* unpack bits, MSB first */
+
+    bit = 7; byte = 0;
+    for(i=0; i<bits_per_codec_frame; i++) {
+        f->codec_bits[i] = (f->packed_codec_bits[byte] >> bit) & 0x1;
+        bit--;
+        if (bit < 0) {
+            bit = 7;
+            byte++;
+        }
+    }
+    
+    if (f->mode == FREEDV_MODE_1600) {
+            
+        /* Protect first 12 out of first 16 excitation bits with (23,12) Golay Code:
+
+           0,1,2,3: v[0]..v[1]
+           4,5,6,7: MSB of pitch
+           11,12,13,14: MSB of energy
+
+        */
+
+        data = 0;
+        for(i=0; i<8; i++) {
+            data <<= 1;
+            data |= f->codec_bits[i];
+        }
+        for(i=11; i<15; i++) {
+            data <<= 1;
+            data |= f->codec_bits[i];
+        }
+        codeword1 = golay23_encode(data);
+
+        /* now pack output frame with parity bits at end to make them
+           as far apart as possible from the data they protect.  Parity
+           bits are LSB of the Golay codeword */
+
+        for(i=0; i<bits_per_codec_frame; i++)
+            f->tx_bits[i] = f->codec_bits[i];
+        for(j=0; i<bits_per_codec_frame+11; i++,j++) {
+            f->tx_bits[i] = (codeword1 >> (10-j)) & 0x1;
+        }
+        f->tx_bits[i] = 0; /* spare bit */
+
+        //for(i=0; i<bits_per_codec_frame+12; i++)
+        //    printf("%d\n", f->tx_bits[i]);
+    }
+
+    /* modulate even and odd frames */
+
+    fdmdv_mod(f->fdmdv, tx_fdm, f->tx_bits, &f->tx_sync_bit);
+    assert(f->tx_sync_bit == 1);
+
+    fdmdv_mod(f->fdmdv, &tx_fdm[FDMDV_NOM_SAMPLES_PER_FRAME], &f->tx_bits[bits_per_fdmdv_frame], &f->tx_sync_bit);
+    assert(f->tx_sync_bit == 0);
+
+    for(i=0; i<2*FDMDV_NOM_SAMPLES_PER_FRAME; i++)
+        mod_out[i] = FDMDV_SCALE * tx_fdm[i].real;
+
+    assert(2*FDMDV_NOM_SAMPLES_PER_FRAME == FREEDV_NSAMPLES);
+}
+
+int freedv_nin(struct freedv *f) {
+    return f->nin;
+}
+
+/*---------------------------------------------------------------------------*\
+                                                       
+  FUNCTION....: freedv_rx
+  AUTHOR......: David Rowe                           
+  DATE CREATED: 3 August 2014
+
+  Takes a frame of samples from the radio receiver, demodulates them,
+  then decodes them, producing a frame of decoded speech samples.  
+
+  To account for difference in the transmit and receive sample clock
+  frequencies, the number of demod_in[] samples is time varying.  It
+  is the responsibility of the caller to pass the correct number of
+  samples.  Call freedv_nin() before each call to freedv_rx() to
+  determine how many samples to pass to this function (see example).
+
+  Returns the number of output speech samples available in
+  speech_out[]. When in sync this will typically alternate between 0
+  and FREEDV_NSAMPLES.  When out of sync, this will be f->nin.
+
+\*---------------------------------------------------------------------------*/
+
+int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) {
+    COMP                rx_fdm[FDMDV_MAX_SAMPLES_PER_FRAME];
+    int                 bits_per_codec_frame, bytes_per_codec_frame, bits_per_fdmdv_frame;
+    int                 reliable_sync_bit, i, j, bit, byte, nin_prev, nout;
+    int                 recd_codeword, codeword1;
+    struct FDMDV_STATS  fdmdv_stats;
+
+    bits_per_codec_frame  = codec2_bits_per_frame(f->codec2);
+    bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8;
+    bits_per_fdmdv_frame  = fdmdv_bits_per_frame(f->fdmdv);
+
+    for(i=0; i<f->nin; i++) {
+        rx_fdm[i].real = (float)demod_in[i]/FDMDV_SCALE;
+        rx_fdm[i].imag = 0;
+    }
+
+    nin_prev = f->nin;
+    fdmdv_demod(f->fdmdv, f->fdmdv_bits, &reliable_sync_bit, rx_fdm, &f->nin);
+    fdmdv_get_demod_stats(f->fdmdv, &fdmdv_stats);
+    
+    if (fdmdv_stats.sync) {
+        if (reliable_sync_bit == 0) {
+            memcpy(f->rx_bits, f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int));
+            nout = 0;
+        }
+        else {
+            memcpy(&f->rx_bits[bits_per_fdmdv_frame], f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int));
+   
+            if (f->mode == FREEDV_MODE_1600) {
+                recd_codeword = 0;
+                for(i=0; i<8; i++) {
+                    recd_codeword <<= 1;
+                    recd_codeword |= f->rx_bits[i];
+                }
+                for(i=11; i<15; i++) {
+                    recd_codeword <<= 1;
+                    recd_codeword |= f->rx_bits[i];
+                }
+                for(i=bits_per_codec_frame; i<bits_per_codec_frame+11; i++) {
+                    recd_codeword <<= 1;
+                    recd_codeword |= f->rx_bits[i];
+                }
+                codeword1 = golay23_decode(recd_codeword);
+                //codeword1 = recd_codeword;
+                //fprintf(stderr, "received codeword1: 0x%x  decoded codeword1: 0x%x\n", recd_codeword, codeword1);
+           
+                for(i=0; i<bits_per_codec_frame; i++)
+                    f->codec_bits[i] = f->rx_bits[i];
+
+                for(i=0; i<8; i++) {
+                    f->codec_bits[i] = (codeword1 >> (22-i)) & 0x1;
+                }
+                for(i=8,j=11; i<12; i++,j++) {
+                    f->codec_bits[j] = (codeword1 >> (22-i)) & 0x1;
+                }
+            }
+
+            // pack bits, MSB received first
+
+            bit  = 7;
+            byte = 0;
+            memset(f->packed_codec_bits, 0,  bytes_per_codec_frame);
+            for(i=0; i<bits_per_codec_frame; i++) {
+                f->packed_codec_bits[byte] |= (f->codec_bits[i] << bit);
+                bit--;
+                if(bit < 0) {
+                    bit = 7;
+                    byte++;
+                }
+            }
+
+            codec2_decode(f->codec2, speech_out, f->packed_codec_bits);
+
+            /* squelch if beneath SNR threshold */
+
+            if (fdmdv_stats.snr_est < f->snr_thresh) {
+                for(i=0; i<FREEDV_NSAMPLES; i++)
+                    speech_out[i] = 0;
+            }
+
+            nout = FREEDV_NSAMPLES;
+        }
+    } /* if (sync) .... */
+    else {
+        /* if not in sync pass through analog samples */
+        /* this lets us "hear" whats going on, e.g. during tuning */
+        for(i=0; i<nin_prev; i++)
+            speech_out[i] = demod_in[i];
+        nout = nin_prev;
+    }
+
+    return nout;
+}
+
+/*---------------------------------------------------------------------------*\
+                                                       
+  FUNCTION....: freedv_tx_text
+  AUTHOR......: David Rowe                           
+  DATE CREATED: 3 August 2014
+
+  Attempt to transmit an ASCII character.  Will return non-zero if
+  character was accepted, zero if the (single character) buffer is
+  full.
+
+\*---------------------------------------------------------------------------*/
+
+int freedv_tx_text(struct freedv *mode, char c) {
+    return 0;
+}
+
+/*---------------------------------------------------------------------------*\
+                                                       
+  FUNCTION....: freedv_tx_text
+  AUTHOR......: David Rowe                           
+  DATE CREATED: 3 August 2014
+
+  Attempt to receive an ASCII character.  Will return non-zero if a new
+  character is available, zero otherwise.
+
+\*---------------------------------------------------------------------------*/
+
+int freedv_rx_text(struct freedv *mode, char *c) {
+    return 0;
+}
+
diff --git a/codec2-dev/src/freedv_api.h b/codec2-dev/src/freedv_api.h
new file mode 100644 (file)
index 0000000..b126c49
--- /dev/null
@@ -0,0 +1,58 @@
+/*---------------------------------------------------------------------------*\
+                                                                             
+  FILE........: freedv_api.h
+  AUTHOR......: David Rowe
+  DATE CREATED: August 2014
+                                                                             
+  Library of API functions that implement FreeDV "modes", useful for
+  embedding FreeDV in other programs.
+                                                                             
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2014 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  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 Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __FREEDV__
+
+
+#define FREEDV_MODE_1600    0
+#define FREEDV_NSAMPLES   320
+
+
+struct freedv {
+    int            mode;
+    void          *codec2;
+    struct FDMDV  *fdmdv;
+    unsigned char *packed_codec_bits;
+    int           *codec_bits;
+    int           *tx_bits;
+    int           *fdmdv_bits;
+    int           *rx_bits;
+    int            tx_sync_bit;
+    float          snr_thresh;
+    int            nin;
+};
+
+
+struct freedv *freedv_open(int mode);
+void freedv_close(struct freedv *freedv);
+void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]);
+int freedv_nin(struct freedv *f);
+int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]);
+
+
+#endif
diff --git a/codec2-dev/src/freedv_rx.c b/codec2-dev/src/freedv_rx.c
new file mode 100644 (file)
index 0000000..8208f8f
--- /dev/null
@@ -0,0 +1,86 @@
+/*---------------------------------------------------------------------------*\
+                                                                             
+  FILE........: freedv_rx.c
+  AUTHOR......: David Rowe
+  DATE CREATED: August 2014
+                                                                             
+  Demo receive program for FreeDV API functions.
+                                                                     
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2014 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  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 Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "freedv_api.h"
+
+int main(int argc, char *argv[]) {
+    FILE          *fin, *fout;
+    short          speech_out[FREEDV_NSAMPLES];
+    short          demod_in[FREEDV_NSAMPLES];
+    struct freedv *freedv;
+    int            nin, nout;
+
+    if (argc < 3) {
+       printf("usage: %s InputModemSpeechFile OutputSpeechawFile\n", argv[0]);
+       printf("e.g    %s hts1a_fdmdv.raw hts1a_out.raw\n", argv[0]);
+       exit(1);
+    }
+
+    if (strcmp(argv[1], "-")  == 0) fin = stdin;
+    else if ( (fin = fopen(argv[1],"rb")) == NULL ) {
+       fprintf(stderr, "Error opening input raw modem sample file: %s: %s.\n",
+         argv[1], strerror(errno));
+       exit(1);
+    }
+
+    if (strcmp(argv[2], "-") == 0) fout = stdout;
+    else if ( (fout = fopen(argv[2],"wb")) == NULL ) {
+       fprintf(stderr, "Error opening output speech sample file: %s: %s.\n",
+         argv[2], strerror(errno));
+       exit(1);
+    }
+    
+    freedv = freedv_open(FREEDV_MODE_1600);
+    assert(freedv != NULL);
+
+    /* Note we need to work out how many samples demod needs on each
+       call (nin).  This is used to adjust for differences in the tx and rx
+       sample clock frequencies.  Note also the number of output
+       speech samples is time varying (nout). */
+
+    nin = freedv_nin(freedv);
+    while(fread(demod_in, sizeof(short), nin, fin) == nin) {
+        nout = freedv_rx(freedv, speech_out, demod_in);
+        fwrite(speech_out, sizeof(short), nout, fout);
+        nin = freedv_nin(freedv);
+    }
+
+    freedv_close(freedv);
+    fclose(fin);
+    fclose(fout);
+
+    return 0;
+}
+
diff --git a/codec2-dev/src/freedv_tx.c b/codec2-dev/src/freedv_tx.c
new file mode 100644 (file)
index 0000000..255d5c7
--- /dev/null
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------*\
+                                                                             
+  FILE........: freedv_tx.c
+  AUTHOR......: David Rowe
+  DATE CREATED: August 2014
+                                                                             
+  Demo transmit program for FreeDV API functions.
+                                                                     
+\*---------------------------------------------------------------------------*/
+
+/*
+  Copyright (C) 2014 David Rowe
+
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1, as
+  published by the Free Software Foundation.  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 Lesser General Public License
+  along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "freedv_api.h"
+
+int main(int argc, char *argv[]) {
+    FILE          *fin, *fout;
+    short          speech_in[FREEDV_NSAMPLES];
+    short          mod_out[FREEDV_NSAMPLES];
+    struct freedv *freedv;
+
+    if (argc < 3) {
+       printf("usage: %s InputRawSpeechFile OutputModemRawFile\n", argv[0]);
+       printf("e.g    %s hts1a.raw hts1a_fdmdv.raw\n", argv[0]);
+       exit(1);
+    }
+
+    if (strcmp(argv[1], "-")  == 0) fin = stdin;
+    else if ( (fin = fopen(argv[1],"rb")) == NULL ) {
+       fprintf(stderr, "Error opening input raw speech sample file: %s: %s.\n",
+         argv[1], strerror(errno));
+       exit(1);
+    }
+
+    if (strcmp(argv[2], "-") == 0) fout = stdout;
+    else if ( (fout = fopen(argv[2],"wb")) == NULL ) {
+       fprintf(stderr, "Error opening output modem sample file: %s: %s.\n",
+         argv[2], strerror(errno));
+       exit(1);
+    }
+    
+    freedv = freedv_open(FREEDV_MODE_1600);
+    assert(freedv != NULL);
+
+    while(fread(speech_in, sizeof(short), FREEDV_NSAMPLES, fin) == FREEDV_NSAMPLES) {
+        freedv_tx(freedv, mod_out, speech_in);
+        fwrite(mod_out, sizeof(short), FREEDV_NSAMPLES, fout);
+    }
+
+    freedv_close(freedv);
+    fclose(fin);
+    fclose(fout);
+
+    return 0;
+}
+