From a6f6209826953fa5c750e749895bf1c04ba0c8d0 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Sat, 4 Jan 2020 18:53:22 -0800 Subject: [PATCH] Initial Commit Downloaded from http://www.aishub.net/downloads/aisdecoder-1.0.0.tar.gz --- CMakeLists.txt | 105 +++++++++ config.h.in | 3 + src/lib/alsaaudio/alsaaudio.c | 119 ++++++++++ src/lib/alsaaudio/alsaaudio.h | 40 ++++ src/lib/callbacks.h | 20 ++ src/lib/filter-i386.h | 275 +++++++++++++++++++++++ src/lib/filter.c | 145 ++++++++++++ src/lib/filter.h | 72 ++++++ src/lib/hmalloc.c | 74 +++++++ src/lib/hmalloc.h | 42 ++++ src/lib/protodec.c | 369 +++++++++++++++++++++++++++++++ src/lib/protodec.h | 65 ++++++ src/lib/pulseaudio/pulseaudio.c | 52 +++++ src/lib/pulseaudio/pulseaudio.h | 39 ++++ src/lib/receiver.c | 147 ++++++++++++ src/lib/receiver.h | 59 +++++ src/lib/winmmaudio/winmm_input.c | 107 +++++++++ src/lib/winmmaudio/winmm_input.h | 23 ++ src/main.c | 347 +++++++++++++++++++++++++++++ src/sounddecoder.c | 231 +++++++++++++++++++ src/sounddecoder.h | 45 ++++ 21 files changed, 2379 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 config.h.in create mode 100755 src/lib/alsaaudio/alsaaudio.c create mode 100644 src/lib/alsaaudio/alsaaudio.h create mode 100644 src/lib/callbacks.h create mode 100644 src/lib/filter-i386.h create mode 100644 src/lib/filter.c create mode 100644 src/lib/filter.h create mode 100644 src/lib/hmalloc.c create mode 100644 src/lib/hmalloc.h create mode 100644 src/lib/protodec.c create mode 100644 src/lib/protodec.h create mode 100644 src/lib/pulseaudio/pulseaudio.c create mode 100644 src/lib/pulseaudio/pulseaudio.h create mode 100755 src/lib/receiver.c create mode 100644 src/lib/receiver.h create mode 100755 src/lib/winmmaudio/winmm_input.c create mode 100755 src/lib/winmmaudio/winmm_input.h create mode 100755 src/main.c create mode 100755 src/sounddecoder.c create mode 100755 src/sounddecoder.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ceab36b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,105 @@ +project(aisdecoder) +cmake_minimum_required(VERSION 2.8) +INCLUDE(CheckIncludeFiles) + +set(STATIC_LIB "${PROJECT_NAME}_static_lib") +set(DYNAMIC_LIB "${PROJECT_NAME}_dynamic_lib") + +macro(get_WIN32_WINNT version) + if (WIN32 AND CMAKE_SYSTEM_VERSION) + set(ver ${CMAKE_SYSTEM_VERSION}) + string(REPLACE "." "" ver ${ver}) + string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver}) + set(${version} "0x${ver}") + endif() +endmacro() + +if(NOT WIN32) + CHECK_INCLUDE_FILES(alsa/asoundlib.h HAVE_ALSA) + CHECK_INCLUDE_FILES(pulse/pulseaudio.h HAVE_PULSEAUDIO) +else() + get_WIN32_WINNT(ver) + if(${CMAKE_SYSTEM_VERSION} LESS 5.1) + MESSAGE(FATAL_ERROR "Windows XP or higher is required") + endif() + add_definitions(-D_WIN32_WINNT=${ver}) +endif() + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE "Debug") +endif() + +if(NOT $ENV{CMAKE_BUILD_TYPE} STREQUAL "") + set(CMAKE_BUILD_TYPE $ENV{CMAKE_BUILD_TYPE}) +endif() + +string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE) +MESSAGE(STATUS "Build configuration: ${CMAKE_BUILD_TYPE}") + +set(CMAKE_C_FLAGS_DEBUG "-g -Wall") +set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall") + +if(${CMAKE_BUILD_TYPE} STREQUAL "RELEASE") + set(CMAKE_EXE_LINKER_FLAGS "-s") +endif() + +if(NOT WIN32) + if(NOT HAVE_ALSA AND NOT HAVE_PULSEAUDIO) + MESSAGE(FATAL_ERROR "The development files for ALSA or PulseAudio are required - libasound-dev, libpulse-dev") + endif() +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/lib) + +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST) +set(SRC_LIST_LIB "${CMAKE_CURRENT_SOURCE_DIR}/src/sounddecoder.c") +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src/lib SRC_LIST_LIB) + +if(NOT WIN32) + if(HAVE_PULSEAUDIO) + aux_source_directory(src/lib/pulseaudio SRC_LIST_LIB) + set(L_PULSE pulse-simple) + else() + message(WARNING "You do not have the development files for pulseaudio (DISABLE).") + endif() + + if(HAVE_ALSA) + aux_source_directory(src/lib/alsaaudio SRC_LIST_LIB) + set(L_ALSA asound) + else() + message(WARNING "You do not have the development files for ASLA (DISABLE).") + endif() +else() + aux_source_directory(src/lib/winmmaudio SRC_LIST_LIB) +endif() + +add_library(${DYNAMIC_LIB} SHARED ${SRC_LIST_LIB}) +set_target_properties(${DYNAMIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) + +add_library(${STATIC_LIB} STATIC ${SRC_LIST_LIB}) +set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) + +if(WIN32) + target_link_libraries(${DYNAMIC_LIB} winmm) +else() + set(MYLIB_VERSION_MAJOR 1) + set(MYLIB_VERSION_MINOR 0) + set(MYLIB_VERSION_PATCH 0) + set(MYLIB_VERSION_STRING ${MYLIB_VERSION_MAJOR}.${MYLIB_VERSION_MINOR}.${MYLIB_VERSION_PATCH}) + set_target_properties(${DYNAMIC_LIB} + PROPERTIES + VERSION ${MYLIB_VERSION_STRING} + SOVERSION ${MYLIB_VERSION_MAJOR}) +endif() + +add_executable(${PROJECT_NAME} ${SRC_LIST}) +target_link_libraries(${PROJECT_NAME} ${STATIC_LIB}) + +if(NOT WIN32) + target_link_libraries(${PROJECT_NAME} ${L_ALSA} ${L_PULSE}) +else() + target_link_libraries(${PROJECT_NAME} winmm ws2_32) +endif() diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..5236f91 --- /dev/null +++ b/config.h.in @@ -0,0 +1,3 @@ +#cmakedefine PROJECT_NAME "${PROJECT_NAME}" +#cmakedefine HAVE_ALSA +#cmakedefine HAVE_PULSEAUDIO diff --git a/src/lib/alsaaudio/alsaaudio.c b/src/lib/alsaaudio/alsaaudio.c new file mode 100755 index 0000000..2eebae6 --- /dev/null +++ b/src/lib/alsaaudio/alsaaudio.c @@ -0,0 +1,119 @@ + +/* + * input.c + * + * (c) Ruben Undheim 2008 + * + * 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 + */ + + +#include + +#include "config.h" +#include "alsaaudio.h" +#include "hmalloc.h" + +#ifdef DMALLOC +#include +#endif + +int input_initialize(snd_pcm_t * handle, short **buffer, int *buffer_l, int channels_num, char *error_msg) +{ + int err; + int dir; + + snd_pcm_hw_params_t *hwparams = NULL; + + snd_pcm_hw_params_alloca(&hwparams); + + if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) { + sprintf(error_msg, "Error initializing hwparams"); + return -1; + } + + if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + sprintf(error_msg, "Error setting acecss mode (SND_PCM_ACCESS_RW_INTERLEAVED): %s", snd_strerror(err)); + return -1; + } + if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) { + sprintf(error_msg, "Error setting format (SND_PCM_FORMAT_S16_LE): %s", snd_strerror(err)); + return -1; + } + + if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, channels_num)) < 0) { + sprintf(error_msg, "Error setting channels %d: %s", channels_num, snd_strerror(err)); + return -1; + } + + unsigned int rate = 48000; + if ((err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, 0)) < 0) { + sprintf(error_msg, "Error setting sample rate (%d): %s", rate, snd_strerror(err)); + return -1; + } + + snd_pcm_uframes_t size = 4096; /* number of frames */ + + dir = 0; + if ((err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &size, &dir)) < 0) { + sprintf(error_msg, "Error setting buffer size (%lu): %s", size, snd_strerror(err)); + return -1; + } + + if ((err = snd_pcm_hw_params(handle, hwparams)) < 0) { + sprintf(error_msg, "Error writing hwparams: %s", snd_strerror(err)); + return -1; + } + + snd_pcm_hw_params_get_period_size(hwparams, &size, &dir); + //int extra = (int) size % 5; + //*buffer_l = (int) size - extra; + *buffer_l = (int) size; + int buffer_len_in_bytes = *buffer_l * sizeof(short) * channels_num; + +// hlog(LOG_DEBUG, LOGPREFIX "Using sound buffer size: %d frames of %d channels: %d bytes", +// *buffer_l, channels, buffer_len_in_bytes); + + *buffer = (short *) hmalloc(buffer_len_in_bytes); + bzero(*buffer, buffer_len_in_bytes); + + return 0; +} + + +int alsa_read(snd_pcm_t * handle, short *buffer, int count) +{ + int err; + + err = snd_pcm_readi(handle, buffer, count); + + if (err == -EPIPE) { +// hlog(LOG_ERR, LOGPREFIX "Overrun"); + snd_pcm_prepare(handle); + } else if (err < 0) { +// hlog(LOG_ERR, LOGPREFIX "Read error"); + } else if (err != count) { +// hlog(LOG_INFO, LOGPREFIX "Short read, read %d frames", err); + } else { + /*hlog(LOG_DEBUG, LOGPREFIX "Read %d samples", err); */ + } + + return err; +} + +void input_cleanup(snd_pcm_t *handle) +{ + snd_pcm_close(handle); +} diff --git a/src/lib/alsaaudio/alsaaudio.h b/src/lib/alsaaudio/alsaaudio.h new file mode 100644 index 0000000..24e6a95 --- /dev/null +++ b/src/lib/alsaaudio/alsaaudio.h @@ -0,0 +1,40 @@ + +/* + * input.h + * + * (c) Ruben Undheim 2008 + * + * 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 + */ + +#ifndef INC_INPUT_H +#define INC_INPUT_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int input_initialize(snd_pcm_t * handle, short **, int *, int channels_num, char *error_msg); +int alsa_read(snd_pcm_t * handle, short *buffer, int count); +void input_cleanup(snd_pcm_t *handle); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/lib/callbacks.h b/src/lib/callbacks.h new file mode 100644 index 0000000..c818762 --- /dev/null +++ b/src/lib/callbacks.h @@ -0,0 +1,20 @@ +#ifndef CALLBACKS_H +#define CALLBACKS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*receiver_on_level_changed)(float level, int channel, unsigned char high); +typedef void (*decoder_on_nmea_sentence_received)(const char *sentence, + unsigned int length, + unsigned char sentences, + unsigned char sentencenum); + +extern receiver_on_level_changed on_sound_level_changed; +extern decoder_on_nmea_sentence_received on_nmea_sentence_received; + +#ifdef __cplusplus +} +#endif +#endif // CALLBACKS_H diff --git a/src/lib/filter-i386.h b/src/lib/filter-i386.h new file mode 100644 index 0000000..0cde026 --- /dev/null +++ b/src/lib/filter-i386.h @@ -0,0 +1,275 @@ +/* + * filter-i386.h -- optimized filter routines + * + * Copyright (C) 1996 + * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* ---------------------------------------------------------------------- */ + +#ifndef _FILTER_I386_H +#define _FILTER_I386_H + +/* ---------------------------------------------------------------------- */ + +#define __HAVE_ARCH_MAC +#define mac(a,b,size) \ +(__builtin_constant_p(size) ? __mac_c((a),(b),(size)) : __mac_g((a),(b),(size))) + +#include + +extern inline float __mac_g(const float *a, const float *b, + unsigned int size) +{ + float sum = 0; + unsigned int i; + + for (i = 0; i < size; i++) + sum += (*a++) * (*b++); + return sum; +} + +extern inline float __mac_c(const float *a, const float *b, + unsigned int size) +{ + float f; + + /* + * inspired from Phil Karn, KA9Q's home page + */ + switch (size) { + case 53: + asm volatile ("flds (%1);\n\t" + "fmuls (%2);\n\t" + "flds 4(%1);\n\t" + "fmuls 4(%2);\n\t" + "flds 8(%1);\n\t" + "fmuls 8(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 12(%1);\n\t" + "fmuls 12(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 16(%1);\n\t" + "fmuls 16(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 20(%1);\n\t" + "fmuls 20(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 24(%1);\n\t" + "fmuls 24(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 28(%1);\n\t" + "fmuls 28(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 32(%1);\n\t" + "fmuls 32(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 36(%1);\n\t" + "fmuls 36(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 40(%1);\n\t" + "fmuls 40(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 44(%1);\n\t" + "fmuls 44(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 48(%1);\n\t" + "fmuls 48(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 52(%1);\n\t" + "fmuls 52(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 56(%1);\n\t" + "fmuls 56(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 60(%1);\n\t" + "fmuls 60(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 64(%1);\n\t" + "fmuls 64(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 68(%1);\n\t" + "fmuls 68(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 72(%1);\n\t" + "fmuls 72(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 76(%1);\n\t" + "fmuls 76(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 80(%1);\n\t" + "fmuls 80(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 84(%1);\n\t" + "fmuls 84(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 88(%1);\n\t" + "fmuls 88(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 92(%1);\n\t" + "fmuls 92(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 96(%1);\n\t" + "fmuls 96(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 100(%1);\n\t" + "fmuls 100(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 104(%1);\n\t" + "fmuls 104(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 108(%1);\n\t" + "fmuls 108(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 112(%1);\n\t" + "fmuls 112(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 116(%1);\n\t" + "fmuls 116(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 120(%1);\n\t" + "fmuls 120(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 124(%1);\n\t" + "fmuls 124(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 128(%1);\n\t" + "fmuls 128(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 132(%1);\n\t" + "fmuls 132(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 136(%1);\n\t" + "fmuls 136(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 140(%1);\n\t" + "fmuls 140(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 144(%1);\n\t" + "fmuls 144(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 148(%1);\n\t" + "fmuls 148(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 152(%1);\n\t" + "fmuls 152(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 156(%1);\n\t" + "fmuls 156(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 160(%1);\n\t" + "fmuls 160(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 164(%1);\n\t" + "fmuls 164(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 168(%1);\n\t" + "fmuls 168(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 172(%1);\n\t" + "fmuls 172(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 176(%1);\n\t" + "fmuls 176(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 180(%1);\n\t" + "fmuls 180(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 184(%1);\n\t" + "fmuls 184(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 188(%1);\n\t" + "fmuls 188(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 192(%1);\n\t" + "fmuls 192(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 196(%1);\n\t" + "fmuls 196(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 200(%1);\n\t" + "fmuls 200(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 204(%1);\n\t" + "fmuls 204(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "flds 208(%1);\n\t" + "fmuls 208(%2);\n\t" + "fxch %%st(2);\n\t" + "faddp;\n\t" + "faddp;\n\t":"=t" (f):"r"(a), "r"(b):"memory"); + return f; + + default: + fprintf(stderr, + "Warning: optimize __mac_c(..., ..., %d)\n", size); + return __mac_g(a, b, size); + } +} + +/* ---------------------------------------------------------------------- */ +#endif /* _FILTER_I386_H */ diff --git a/src/lib/filter.c b/src/lib/filter.c new file mode 100644 index 0000000..3909dd3 --- /dev/null +++ b/src/lib/filter.c @@ -0,0 +1,145 @@ +/* + * filter.c -- FIR filter + * + * Copyright (C) 2001, 2002, 2003 + * Tomi Manninen (oh2bns@sral.fi) + * + * This file is part of gMFSK. + * + * gMFSK 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. + * + * gMFSK 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 gMFSK; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "hmalloc.h" +#include "filter.h" + +#undef DEBUG + +#ifdef DEBUG +#include +#endif + +/* ---------------------------------------------------------------------- */ + +/* + * This gets used when not optimising + */ +#ifndef __OPTIMIZE__ +float filter_mac(const float *a, const float *b, int size) +{ + float sum = 0; + int i; + + for (i = 0; i < size; i++) + sum += a[i] * b[i]; + + return sum; +} +#endif + +/* ---------------------------------------------------------------------- */ + +struct filter *filter_init(int len, float *taps) +{ + struct filter *f; + + f = (struct filter *) hmalloc(sizeof(struct filter)); + memset(f, 0, sizeof(struct filter)); + + f->taps = (float *) hmalloc(len * sizeof(float)); + memcpy(f->taps, taps, len * sizeof(float)); + + f->length = len; + f->pointer = f->length; + + return f; +} + +void filter_free(struct filter *f) +{ + if (f) { + hfree(f->taps); + hfree(f); + } +} + +/* ---------------------------------------------------------------------- */ + +void filter_run(struct filter *f, float in, float *out) +{ + float *ptr = f->buffer + f->pointer++; + + *ptr = in; + + // TODO: optimize: pass filter length as constant to enable + // using optimized __mac_c and fix the number of rounds there! + #ifndef __HAVE_ARCH_MAC + *out = filter_mac(ptr - f->length, f->taps, f->length); + #else + *out = mac(ptr - f->length, f->taps, f->length); + #endif + //*out = filter_mac(ptr - f->length, f->taps, 53); + + if (f->pointer == BufferLen) { + memcpy(f->buffer, + f->buffer + BufferLen - f->length, + f->length * sizeof(float)); + f->pointer = f->length; + } +} + +short filter_run_buf(struct filter *f, short *in, float *out, int step, int len) +{ + int id = 0; + int od = 0; + short maxval = 0; + int pointer = f->pointer; + float *buffer = f->buffer; + + while (od < len) { + buffer[pointer] = in[id]; + + // look for peak volume + if (in[id] > maxval) + maxval = in[id]; + + #ifndef __HAVE_ARCH_MAC + out[od] = filter_mac(&buffer[pointer - f->length], f->taps, f->length); + #else + out[od] = mac(&buffer[pointer - f->length], f->taps, f->length); + #endif + pointer++; + + /* the buffer is much smaller than the incoming chunks */ + if (pointer == BufferLen) { + memcpy(buffer, + buffer + BufferLen - f->length, + f->length * sizeof(float)); + pointer = f->length; + } + + id += step; + od++; + } + + f->pointer = pointer; + + return maxval; +} + +/* ---------------------------------------------------------------------- */ diff --git a/src/lib/filter.h b/src/lib/filter.h new file mode 100644 index 0000000..beac695 --- /dev/null +++ b/src/lib/filter.h @@ -0,0 +1,72 @@ +/* + * filter.h -- FIR filter + * + * Copyright (C) 2001, 2002, 2003, 2004 + * Tomi Manninen (oh2bns@sral.fi) + * + * This file is part of gMFSK. + * + * gMFSK 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. + * + * gMFSK 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 gMFSK; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _FILTER_H +#define _FILTER_H + +#define BufferLen 1024 + +/* ---------------------------------------------------------------------- */ + +#ifdef __OPTIMIZE__ + +#ifdef __i386__ +#include "filter-i386.h" +#endif /* __i386__ */ + + +#ifndef __HAVE_ARCH_MAC +static __inline__ float filter_mac(const float *a, const float *b, int size) +{ + float sum = 0; + int i; + + for (i = 0; i < size; i++) + sum += a[i] * b[i]; + + return sum; +} +#endif /* __HAVE_ARCH_MAC */ + +#endif /* __OPTIMIZE__ */ + + +/* ---------------------------------------------------------------------- */ + +struct filter { + int length; + float *taps; + float buffer[BufferLen]; + int pointer; +}; + +extern struct filter *filter_init(int len, float *taps); +extern void filter_free(struct filter *f); + +extern void filter_run(struct filter *f, float in, float *out); +extern short filter_run_buf(struct filter *f, short *in, float *out, int step, int len); + +/* ---------------------------------------------------------------------- */ + +#endif /* _FILTER_H */ diff --git a/src/lib/hmalloc.c b/src/lib/hmalloc.c new file mode 100644 index 0000000..910d238 --- /dev/null +++ b/src/lib/hmalloc.c @@ -0,0 +1,74 @@ +/* + * (c) Heikki Hannikainen, OH7LZB + * + * 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 + * + */ + +/* + * Replacements for malloc, realloc and free, which never fail, + * and might keep statistics on memory allocation... + * + * GPL'ed, by Heikki Hannikainen + */ + +#include +#include +#include + +#include "hmalloc.h" + +#ifdef DMALLOC +#include +#endif + +int mem_panic = 0; + +void *hmalloc(size_t size) { + void *p; + if (!(p = malloc(size))) { + if (mem_panic) exit(1); + mem_panic = 1; + exit(1); + } + + return p; +} + +void *hrealloc(void *ptr, size_t size) { + void *p; + + if (!(p = realloc(ptr, size))) { + if (mem_panic) exit(1); + mem_panic = 1; + exit(1); + } + + return p; +} + +void hfree(void *ptr) { + if (ptr) free(ptr); +} + +char *hstrdup(const char *s) { + char *p; + + p = (char*)hmalloc(strlen(s)+1); + strcpy(p, s); + + return p; +} + diff --git a/src/lib/hmalloc.h b/src/lib/hmalloc.h new file mode 100644 index 0000000..30007b0 --- /dev/null +++ b/src/lib/hmalloc.h @@ -0,0 +1,42 @@ +/* + * (c) Heikki Hannikainen, OH7LZB + * + * 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 + * + */ + +#ifndef HMALLOC_H +#define HMALLOC_H + +#include +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Replacements for malloc, realloc and free, which never fail, + * and might keep statistics on memory allocation... + */ + +extern void *hmalloc(size_t size); +extern void *hrealloc(void *ptr, size_t size); +extern void hfree(void *ptr); + +extern char *hstrdup(const char *s); +#ifdef __cplusplus +} +#endif +#endif + diff --git a/src/lib/protodec.c b/src/lib/protodec.c new file mode 100644 index 0000000..39ac2aa --- /dev/null +++ b/src/lib/protodec.c @@ -0,0 +1,369 @@ + +/* + * protodec.c + * + * (c) Ruben Undheim 2008 + * + * 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 + */ + +#include +#include +#include /* String function definitions */ +#include "callbacks.h" +#include "config.h" + +#include "protodec.h" +#include "hmalloc.h" + +decoder_on_nmea_sentence_received on_nmea_sentence_received=NULL; + +#ifdef DMALLOC +#include +#endif + +void protodec_initialize(struct demod_state_t *d, struct serial_state_t *serial, char chanid) +{ + memset(d, 0, sizeof(struct demod_state_t)); + + d->chanid = chanid; + d->serial = serial; + + d->receivedframes = 0; + d->lostframes = 0; + d->lostframes2 = 0; + + protodec_reset(d); + + d->seqnr = 0; + + d->buffer = hmalloc(DEMOD_BUFFER_LEN); + d->rbuffer = hmalloc(DEMOD_BUFFER_LEN); + d->nmea = hmalloc(NMEABUFFER_LEN); +} + +void protodec_deinit(struct demod_state_t *d) +{ + hfree(d->buffer); + hfree(d->rbuffer); + hfree(d->nmea); +} + +void protodec_reset(struct demod_state_t *d) +{ + d->state = ST_SKURR; + d->nskurr = 0; + d->ndata = 0; + d->npreamble = 0; + d->nstartsign = 0; + d->nstopsign = 0; + d->antallpreamble = 0; + d->antallenner = 0; + d->last = 0; + d->bitstuff = 0; + d->bufferpos = 0; +} + +/* + * Calculates CRC-checksum + */ + +unsigned short protodec_sdlc_crc(const unsigned char *data, unsigned len) +{ + unsigned short c, crc = 0xffff; + + while (len--) + for (c = 0x100 + *data++; c > 1; c >>= 1) + if ((crc ^ c) & 1) + crc = (crc >> 1) ^ 0x8408; + else + crc >>= 1; + return ~crc; + +} + +int protodec_calculate_crc(int length_bits, struct demod_state_t *d) +{ + int length_bytes; + unsigned char *buf; + int buflen; + int i, j, x; + unsigned char tmp; + + if (length_bits <= 0) { + return 0; + } + + length_bytes = length_bits / 8; + buflen = length_bytes + 2; + + /* what is this? */ + buf = (unsigned char *) hmalloc(sizeof(*buf) * buflen); + for (j = 0; j < buflen; j++) { + tmp = 0; + for (i = 0; i < 8; i++) + tmp |= (((d->buffer[i + 8 * j]) << (i))); + buf[j] = tmp; + } + + /* ok, here's the actual CRC calculation */ + unsigned short crc = protodec_sdlc_crc(buf, buflen); + //DBG(printf("CRC: %04x\n",crc)); + + /* what is this? */ + memset(d->rbuffer, 0, DEMOD_BUFFER_LEN); + for (j = 0; j < length_bytes; j++) { + for (i = 0; i < 8; i++) { + x = j * 8 + i; + if (x >= DEMOD_BUFFER_LEN) { + hfree(buf); + return 0; + } else { + d->rbuffer[x] = (buf[j] >> (7 - i)) & 1; + } + } + } + + hfree(buf); + + return (crc == 0x0f47); +} + +unsigned long protodec_henten(int from, int size, unsigned char *frame) +{ + int i = 0; + unsigned long tmp = 0; + + for (i = 0; i < size; i++) + tmp |= (frame[from + i]) << (size - 1 - i); + + return tmp; +} + + +void protodec_generate_nmea(struct demod_state_t *d, int bufferlen, int fillbits, time_t received_t) +{ + int senlen; + int pos; + int k, offset; + int m; + unsigned char sentences, sentencenum, nmeachk, letter; + + //6bits to nmea-ascii. One sentence len max 82char + //inc. head + tail.This makes inside datamax 62char multipart, 62 single + senlen = 56; //this is normally not needed.For testing only. May be fixed number + if (bufferlen <= (senlen * 6)) { + sentences = 1; + } else { + sentences = bufferlen / (senlen * 6); + //sentences , if overflow put one more + if (bufferlen % (senlen * 6) != 0) + sentences++; + }; + + sentencenum = 0; + pos = 0; + offset = (sentences>1) ? 15 : 14; + do { + k = offset; //leave room for nmea header + while (k < senlen + offset && bufferlen > pos) { + letter = (unsigned char)protodec_henten(pos, 6, d->rbuffer); + // 6bit-to-ascii conversion by IEC + letter += (letter < 40) ? 48 : 56; + d->nmea[k] = letter; + pos += 6; + k++; + } + sentencenum++; + + memcpy(&d->nmea[0], "!AIVDM,0,0,", 11); + d->nmea[7] += sentences; + d->nmea[9] += sentencenum; + + memcpy(&d->nmea[k], ",0*00\0", 6); + + if (sentences > 1) { + d->nmea[11] = '0' + d->seqnr; + d->nmea[12] = ','; + d->nmea[13] = d->chanid; + d->nmea[14] = ','; + if (sentencenum == sentences) d->nmea[k + 1] = '0' + fillbits; + } else { + d->nmea[11] = ','; + d->nmea[12] = d->chanid; + d->nmea[13] = ','; + } + + m = 1; + nmeachk = d->nmea[m++]; + while (d->nmea[m] != '*') nmeachk ^= d->nmea[m++]; + + sprintf(&d->nmea[k + 3], "%02X\r\n", nmeachk); + if (on_nmea_sentence_received != NULL) + on_nmea_sentence_received(d->nmea, k+7, sentences, sentencenum); + } while (sentencenum < sentences); +} + +void protodec_getdata(int bufferlen, struct demod_state_t *d) +{ + unsigned char type = protodec_henten(0, 6, d->rbuffer); + if (type < 1 || type > MAX_AIS_PACKET_TYPE /* 9 */) + return; +// unsigned long mmsi = protodec_henten(8, 30, d->rbuffer); + int fillbits = 0; + int k; + time_t received_t; + time(&received_t); + + if (bufferlen % 6 > 0) { + fillbits = 6 - (bufferlen % 6); + for (k = bufferlen; k < bufferlen + fillbits; k++) + d->rbuffer[k] = 0; + + bufferlen = bufferlen + fillbits; + } + + protodec_generate_nmea(d, bufferlen, fillbits, received_t); + + d->seqnr++; + if (d->seqnr > 9) + d->seqnr = 0; + + if (type < 1 || type > MAX_AIS_PACKET_TYPE) + return; // unsupported packet type +} + +void protodec_decode(char *in, int count, struct demod_state_t *d) +{ + int i = 0; + int bufferlength, correct; + + while (i < count) { + switch (d->state) { + case ST_DATA: + if (d->bitstuff) { + if (in[i] == 1) { + d->state = ST_STOPSIGN; + d->ndata = 0; + d->bitstuff = 0; + } else { + d->ndata++; + d->last = in[i]; + d->bitstuff = 0; + } + } else { + if (in[i] == d->last && in[i] == 1) { + d->antallenner++; + if (d->antallenner == 4) { + d->bitstuff = 1; + d->antallenner = 0; + } + + } else + d->antallenner = 0; + + d->buffer[d->bufferpos] = in[i]; + d->bufferpos++; + d->ndata++; + + if (d->bufferpos >= 449) { + protodec_reset(d); + } + } + break; + + case ST_SKURR: + if (in[i] != d->last) + d->antallpreamble++; + else + d->antallpreamble = 0; + d->last = in[i]; + if (d->antallpreamble > 14 && in[i] == 0) { + d->state = ST_PREAMBLE; + d->nskurr = 0; + d->antallpreamble = 0; + } + d->nskurr++; + break; + + case ST_PREAMBLE: + if (in[i] != d->last && d->nstartsign == 0) { + d->antallpreamble++; + } else { + if (in[i] == 1) { + if (d->nstartsign == 0) { + d->nstartsign = 3; + d->last = in[i]; + } else if (d->nstartsign == 5) { + d->nstartsign++; + d->npreamble = 0; + d->antallpreamble = 0; + d->state = ST_STARTSIGN; + } else { + d->nstartsign++; + } + + } else { + if (d->nstartsign == 0) { + d->nstartsign = 1; + } else { + protodec_reset(d); + } + } + } + d->npreamble++; + break; + + case ST_STARTSIGN: + if (d->nstartsign >= 7) { + if (in[i] == 0) { + d->state = ST_DATA; + d->nstartsign = 0; + d->antallenner = 0; + memset(d->buffer, 0, DEMOD_BUFFER_LEN); + d->bufferpos = 0; + } else { + protodec_reset(d); + } + + } else if (in[i] == 0) { + protodec_reset(d); + } + d->nstartsign++; + break; + + case ST_STOPSIGN: + bufferlength = d->bufferpos - 6 - 16; + if (in[i] == 0 && bufferlength > 0) { + correct = protodec_calculate_crc(bufferlength, d); + if (correct) { + d->receivedframes++; + protodec_getdata(bufferlength, d); + } else { + d->lostframes++; + } + } else { + d->lostframes2++; + } + protodec_reset(d); + break; + + + } + d->last = in[i]; + i++; + } +} + diff --git a/src/lib/protodec.h b/src/lib/protodec.h new file mode 100644 index 0000000..84c513e --- /dev/null +++ b/src/lib/protodec.h @@ -0,0 +1,65 @@ + +/* + * protodec.h + * + * (c) Ruben Undheim 2008 + * + * 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 + */ + + +#ifndef INC_PROTODEC_H +#define INC_PROTODEC_H + +#define ST_SKURR 1 +#define ST_PREAMBLE 2 +#define ST_STARTSIGN 3 +#define ST_DATA 4 +#define ST_STOPSIGN 5 + +#define DEMOD_BUFFER_LEN 450 +#define MAX_AIS_PACKET_TYPE 27 +#define NMEABUFFER_LEN 100 + +struct demod_state_t { + char chanid; + int state; + unsigned int offset; + int nskurr, npreamble, nstartsign, ndata, nstopsign; + + int antallenner; + unsigned char *buffer; + unsigned char *rbuffer; + char *tbuffer; + int bufferpos; + char last; + int antallpreamble; + int bitstuff; + int receivedframes; + int lostframes; + int lostframes2; + unsigned char seqnr; + + struct serial_state_t *serial; + + char *nmea; +}; + +void protodec_initialize(struct demod_state_t *d, struct serial_state_t *serial, char chanid); +void protodec_reset(struct demod_state_t *d); +void protodec_getdata(int bufferlengde, struct demod_state_t *d); +void protodec_decode(char *in, int count, struct demod_state_t *d); + +#endif diff --git a/src/lib/pulseaudio/pulseaudio.c b/src/lib/pulseaudio/pulseaudio.c new file mode 100644 index 0000000..ef850c4 --- /dev/null +++ b/src/lib/pulseaudio/pulseaudio.c @@ -0,0 +1,52 @@ + +/* + * pulseaudio.c + * + * (c) Ruben Undheim 2012 + * + * 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 + */ + + +#include +#include +#include +#include "pulseaudio.h" + +pa_simple *pulseaudio_initialize(unsigned char channels_number, const char *appname) { + + pa_simple *s; + pa_sample_spec ss; + + ss.format = PA_SAMPLE_S16NE; + ss.channels = channels_number; + ss.rate = 48000; + + s = pa_simple_new(NULL, + (appname != NULL) ? appname : "AIS Demodulator", + PA_STREAM_RECORD, NULL, "AIS data", &ss, NULL, NULL, NULL); + + return s; +} + + +void pulseaudio_cleanup(pa_simple *s){ + pa_simple_free(s); +} + +int pulseaudio_read(pa_simple *s, short *buffer, int count, unsigned char channels_number){ + int number_read = pa_simple_read(s, buffer, (size_t)(count * sizeof(short) * channels_number), NULL); + return (number_read < 0) ? -1 : count; +} diff --git a/src/lib/pulseaudio/pulseaudio.h b/src/lib/pulseaudio/pulseaudio.h new file mode 100644 index 0000000..135f64a --- /dev/null +++ b/src/lib/pulseaudio/pulseaudio.h @@ -0,0 +1,39 @@ + +/* + * pulseaudio.h + * + * (c) Ruben Undheim 2012 + * + * 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 + */ + +#ifndef INC_PULSEAUDIO_H +#define INC_PULSEAUDIO_H + +#include +#ifdef __cplusplus +extern "C" { +#endif + +pa_simple *pulseaudio_initialize(unsigned char channels_number, const char *appname); + +void pulseaudio_cleanup(pa_simple *s); + +int pulseaudio_read(pa_simple *s, short *buffer, int count, unsigned char channels_number); + +#ifdef __cplusplus +} +#endif +#endif /* INC_PULSEAUDIO_H */ diff --git a/src/lib/receiver.c b/src/lib/receiver.c new file mode 100755 index 0000000..6ebcad8 --- /dev/null +++ b/src/lib/receiver.c @@ -0,0 +1,147 @@ + +/* + * receiver.c + * + * (c) Ruben Undheim 2008 + * (c) Heikki Hannikainen 2008 + * + * 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 + */ + + +#include "config.h" + +#include +#include + +#include "receiver.h" +#include "hmalloc.h" +#include "filter.h" + +static int sound_levellog=1; + +receiver_on_level_changed on_sound_level_changed=NULL; + +static float coeffs[]={ + 2.5959e-55, 2.9479e-49, 1.4741e-43, 3.2462e-38, 3.1480e-33, + 1.3443e-28, 2.5280e-24, 2.0934e-20, 7.6339e-17, 1.2259e-13, + 8.6690e-11, 2.6996e-08, 3.7020e-06, 2.2355e-04, 5.9448e-03, + 6.9616e-02, 3.5899e-01, 8.1522e-01, 8.1522e-01, 3.5899e-01, + 6.9616e-02, 5.9448e-03, 2.2355e-04, 3.7020e-06, 2.6996e-08, + 8.6690e-11, 1.2259e-13, 7.6339e-17, 2.0934e-20, 2.5280e-24, + 1.3443e-28, 3.1480e-33, 3.2462e-38, 1.4741e-43, 2.9479e-49, + 2.5959e-55 +}; +#define COEFFS_L 36 + + +struct receiver *init_receiver(char name, int num_ch, int ch_ofs) +{ + struct receiver *rx; + + rx = (struct receiver *) hmalloc(sizeof(struct receiver)); + memset(rx, 0, sizeof(struct receiver)); + + rx->filter = filter_init(COEFFS_L, coeffs); + + rx->decoder = hmalloc(sizeof(struct demod_state_t)); + protodec_initialize(rx->decoder, NULL, name); + + rx->name = name; + rx->lastbit = 0; + rx->num_ch = num_ch; + rx->ch_ofs = ch_ofs; + rx->pll = 0; + rx->pllinc = 0x10000 / 5; + rx->prev = 0; + rx->last_levellog = 0; + + return rx; +} + +void free_receiver(struct receiver *rx) +{ + if (rx) { + filter_free(rx->filter); + hfree(rx); + } +} + +#define INC 16 +#define FILTERED_LEN 8192 + +void receiver_run(struct receiver *rx, short *buf, int len) +{ + float out; + int curr, bit; + char b; + short maxval = 0; + int level_distance; + float level; + int rx_num_ch = rx->num_ch; + float filtered[FILTERED_LEN]; + int i; + + /* len is number of samples available in buffer for each + * channels - something like 1024, regardless of number of channels */ + + buf += rx->ch_ofs; + + if (len > FILTERED_LEN) + abort(); + + maxval = filter_run_buf(rx->filter, buf, filtered, rx_num_ch, len); + + for (i = 0; i < len; i++) { + out = filtered[i]; + curr = (out > 0); + + if ((curr ^ rx->prev) == 1) { + if (rx->pll < (0x10000 / 2)) { + rx->pll += rx->pllinc / INC; + } else { + rx->pll -= rx->pllinc / INC; + } + } + rx->prev = curr; + + rx->pll += rx->pllinc; + + if (rx->pll > 0xffff) { + /* slice */ + bit = (out > 0); + /* nrzi decode */ + b = !(bit ^ rx->lastbit); + /* feed to the decoder */ + protodec_decode(&b, 1, rx->decoder); + + rx->lastbit = bit; + rx->pll &= 0xffff; + } + } + + /* calculate level, and log it */ + level = (float)maxval / (float)32768 * (float)100; + level_distance = time(NULL) - rx->last_levellog; + + if (level > 95.0 && (level_distance >= 30 || level_distance >= sound_levellog)) { + if (on_sound_level_changed != NULL) on_sound_level_changed(level, rx->ch_ofs, 1); + time(&rx->last_levellog); + } else if (sound_levellog != 0 && level_distance >= sound_levellog) { + if (on_sound_level_changed != NULL) on_sound_level_changed(level, rx->ch_ofs, 0); + time(&rx->last_levellog); + } +} + diff --git a/src/lib/receiver.h b/src/lib/receiver.h new file mode 100644 index 0000000..f2b0a5a --- /dev/null +++ b/src/lib/receiver.h @@ -0,0 +1,59 @@ + +/* + * receiver.h + * + * (c) Ruben Undheim 2008 + * (c) Heikki Hannikainen 2008 + * + * 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 + */ + + +#ifndef INC_RECEIVER_H +#define INC_RECEIVER_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#include "protodec.h" +#include "callbacks.h" + +struct receiver { + struct filter *filter; + char name; + int lastbit; + int num_ch; + int ch_ofs; + unsigned int pll; + unsigned int pllinc; + struct demod_state_t *decoder; + int prev; + time_t last_levellog; +}; + +extern struct receiver *init_receiver(char name, int num_ch, int ch_ofs); +extern void free_receiver(struct receiver *rx); + +extern void receiver_run(struct receiver *rx, short *buf, int len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/lib/winmmaudio/winmm_input.c b/src/lib/winmmaudio/winmm_input.c new file mode 100755 index 0000000..2579c71 --- /dev/null +++ b/src/lib/winmmaudio/winmm_input.c @@ -0,0 +1,107 @@ +#include "winmm_input.h" +#include +#include + +#ifdef EXTENSIBLE +#define _KSDATAFORMAT_SUBTYPE_PCM (GUID) {0x00000001,0x0000,0x0010,{0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}} +#endif + +static WAVEHDR wave_chunk[WAVE_BUFFERS]; +static LPWAVEHDR current; +static unsigned int buffer_size=0; + +static void CALLBACK waveInProc( // wave in I/O completion procedure + HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 +) { + switch (uMsg) { + case WIM_DATA: + current = (LPWAVEHDR)dwParam1; + SetEvent((HANDLE)dwInstance); + break; + } +} + +int winmm_init(unsigned int devId, + LPHWAVEIN device, + int channels, + int bytesCount, + HANDLE *eventHandler +) { + + *eventHandler=CreateEvent(NULL, FALSE, FALSE, NULL); + if (*eventHandler == NULL) return 0; + +#ifndef EXTENSIBLE + WAVEFORMATEX Format; + Format.cbSize = 0; + Format.wFormatTag = WAVE_FORMAT_PCM; + Format.nChannels = channels; + Format.wBitsPerSample = 16; + Format.nSamplesPerSec = 48000L; + Format.nBlockAlign = Format.nChannels * Format.wBitsPerSample / 8; + Format.nAvgBytesPerSec = Format.nSamplesPerSec * Format.nBlockAlign; + LPWAVEFORMATEX pFmt=&Format; +#else + WAVEFORMATEXTENSIBLE fmt; + fmt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); + fmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + fmt.Format.nChannels = channels; + fmt.Format.wBitsPerSample = 16; + fmt.Format.nSamplesPerSec = 48000L; + fmt.Format.nBlockAlign = fmt.Format.nChannels * fmt.Format.wBitsPerSample / 8; + fmt.Format.nAvgBytesPerSec = fmt.Format.nSamplesPerSec * fmt.Format.nBlockAlign; + fmt.SubFormat=_KSDATAFORMAT_SUBTYPE_PCM; + fmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + fmt.Samples.wReserved=0; + fmt.Samples.wValidBitsPerSample=16; + LPWAVEFORMATEX pFmt=&fmt.Format; +#endif + + MMRESULT err = waveInOpen(device, devId, pFmt, (DWORD)waveInProc, (DWORD)*eventHandler, CALLBACK_FUNCTION); + if (err == MMSYSERR_NOERROR) { + buffer_size=sizeof(WAVEHDR); + size_t i; + for (i=0; inBlockAlign; + ZeroMemory(wave_chunk[i].lpData, wave_chunk[i].dwBufferLength); + if (waveInPrepareHeader(*device, &wave_chunk[i], buffer_size) == MMSYSERR_NOERROR) { + waveInAddBuffer(*device, &wave_chunk[i], buffer_size); + } else return 0; + } + return 1; + } + return 0; +} + +int winmm_getRecorded(LPHWAVEIN device, char *out) { + int number=current->dwBytesRecorded/current->dwUser; + if (number > 0) { + memcpy(out, current->lpData, current->dwBytesRecorded); + } + waveInUnprepareHeader(*device, current, buffer_size); + current->dwBytesRecorded=0; + current->dwFlags=0; + waveInPrepareHeader(*device, current, buffer_size); + waveInAddBuffer(*device, current, buffer_size); + current=NULL; + return number; +} + +void winmm_cleanup(LPHWAVEIN device, HANDLE *eventHandler) { + if (device != NULL) { + waveInStop(*device); + size_t i; + for (i=0; i +#include +#include + +//#ifdef EXTENSIBLE +#define WAVE_BUFFERS 4 + +#ifdef __cplusplus +extern "C" { +#endif + +int winmm_init(unsigned int devId, + LPHWAVEIN device, + int channels, + int bytesCount, + HANDLE *eventHandler); + +int winmm_getRecorded(LPHWAVEIN device, char *out); +void winmm_cleanup(LPHWAVEIN device, HANDLE *eventHandler); + +#ifdef __cplusplus +} +#endif diff --git a/src/main.c b/src/main.c new file mode 100755 index 0000000..f5d60c0 --- /dev/null +++ b/src/main.c @@ -0,0 +1,347 @@ +/* + * main.cpp -- AIS Decoder + * + * Copyright (C) 2013 + * Astra Paging Ltd / AISHub (info@aishub.net) + * + * AISDecoder 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. + * + * AISDecoder uses parts of GNUAIS project (http://gnuais.sourceforge.net/) + * + */ + +#ifndef WIN32 +#include +#include +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "sounddecoder.h" +#include "callbacks.h" +#ifdef HAVE_ALSA +#include "alsaaudio/alsaaudio.h" +#endif + +#ifndef WIN32 +#ifdef HAVE_ALSA +#define HELP_AUDIO_DEVICE "-D\tALSA input device\n\tUse '-D list' to see all available system devices.\n" +#else +#define HELP_AUDIO_DEVICE "" +#endif +#ifdef HAVE_PULSEAUDIO +#ifdef HAVE_ALSA +#define HELP_AUDIO_DRIVERS "pulse,alsa,file" +#else +#define HELP_AUDIO_DRIVERS "pulse,file" +#endif +#else +#ifdef HAVE_ALSA +#define HELP_AUDIO_DRIVERS "alsa,file" +#endif +#endif +#else +#define HELP_AUDIO_DRIVERS "winmm,file" +#define HELP_AUDIO_DEVICE "-D\tNumeric value for input device (default WAVE_MAPPER)\n\tUse '-D list' to see all available system devices.\n" +#endif + +#define HELP_MSG \ +"Usage: " PROJECT_NAME " -h hostname -p port -a " HELP_AUDIO_DRIVERS " [-f /path/file.raw] [-l]\n\n"\ +"-h\tDestination host or IP address\n"\ +"-p\tDestination UDP port\n"\ +"-a\tAudio driver [" HELP_AUDIO_DRIVERS "]\n" HELP_AUDIO_DEVICE\ +"-c\tSound channels [stereo,mono,left,right] (default stereo)\n"\ +"-f\tFull path to 48kHz raw audio file\n"\ +"-l\tLog sound levels to console (stderr)\n"\ +"-d\tLog NMEA sentences to console (stderr)\n"\ +"-H\tDisplay this help\n" + +#define MAX_BUFFER_LENGTH 2048 +static char buffer[MAX_BUFFER_LENGTH]; +static unsigned int buffer_count=0; + +#ifdef WIN32 +WSADATA wsaData; +void printInDevices() { + unsigned int count = waveInGetNumDevs(); + WAVEINCAPS caps; + unsigned int i = 0; + for (i = 0; i < count; i++) { + if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { + fprintf(stderr, "%u: %s\r\n", i, caps.szPname); + } else { + fprintf(stderr, "Can't read devices\r\n"); + exit(EXIT_FAILURE); + } + } +} +#endif + +#ifdef HAVE_ALSA +void printInDevices() { + char **hints; + /* Enumerate sound devices */ + int err = snd_device_name_hint(0, "pcm", (void***)&hints); + if (err != 0) { + fprintf(stderr, "Can't read data\n"); + exit(EXIT_FAILURE); + } + + char** n = hints; + while (*n != NULL) { + char *name = snd_device_name_get_hint(*n, "NAME"); + char *desc = snd_device_name_get_hint(*n, "DESC"); + char *type = snd_device_name_get_hint(*n, "IOID"); + if (name != NULL && desc != NULL) { + if (type == NULL || !strcmp(type, "Input")) { + printf("%s\n%s: <%s>\n\n", desc, type == NULL ? "Input/Output" : "Input",name); + } + } + if (name != NULL) free(name); + if (name != NULL) free(desc); + if (name != NULL) free(type); + n++; + } + + //Free hint buffer too + snd_device_name_free_hint((void**)hints); + snd_config_update_free_global(); +} + +#endif + +static int sock; +static struct addrinfo* addr=NULL; + +static int initSocket(const char *host, const char *portname); +static int show_levels=0; +static int debug_nmea=0; + +void sound_level_changed(float level, int channel, unsigned char high) { + if (high != 0) + fprintf(stderr, "Level on ch %d too high: %.0f %%\n", channel, level); + else + fprintf(stderr, "Level on ch %d: %.0f %%\n", channel, level); +} + +void nmea_sentence_received(const char *sentence, + unsigned int length, + unsigned char sentences, + unsigned char sentencenum) { + if (sentences == 1) { + if (sendto(sock, sentence, length, 0, addr->ai_addr, addr->ai_addrlen) == -1) abort(); + if (debug_nmea) fprintf(stderr, "%s", sentence); + } else { + if (buffer_count + length < MAX_BUFFER_LENGTH) { + memcpy(&buffer[buffer_count], sentence, length); + buffer_count += length; + } else { + buffer_count=0; + } + + if (sentences == sentencenum && buffer_count > 0) { + if (sendto(sock, buffer, buffer_count, 0, addr->ai_addr, addr->ai_addrlen) == -1) abort(); + if (debug_nmea) fprintf(stderr, "%s", buffer); + buffer_count=0; + }; + } +} + +#define CMD_PARAMS_COMMON "h:p:a:lHf:dc:" +#ifndef WIN32 +#ifdef HAVE_ALSA +#define CMD_PARAMS CMD_PARAMS_COMMON "D:" +#else +#define CMD_PARAMS CMD_PARAMS_COMMON +#endif +#else +#define CMD_PARAMS CMD_PARAMS_COMMON "D:" +#endif + +int main(int argc, char *argv[]) { + Sound_Channels channels = SOUND_CHANNELS_STEREO; + char *host, *port, *file_name=NULL; + const char *params=CMD_PARAMS; + int alsa=0, pulse=0, file=0, winmm=0; + int hfnd=0, pfnd=0, afnd=0; +#ifdef WIN32 + unsigned int deviceId=WAVE_MAPPER; +#endif +#ifdef HAVE_ALSA + char *alsaDevice=NULL; +#endif + int opt; + while ((opt = getopt(argc, argv, params)) != -1) { + switch (opt) { + case 'h': + host = optarg; + hfnd = 1; + break; + case 'p': + port = optarg; + pfnd = 1; + break; + case 'a': + #ifdef HAVE_PULSEAUDIO + pulse = strcmp(optarg, "pulse") == 0; + #endif + #ifdef HAVE_ALSA + alsa = strcmp(optarg, "alsa") == 0; + #endif + #ifdef WIN32 + winmm = strcmp(optarg, "winmm") == 0; + #endif + file = strcmp(optarg, "file") == 0; + afnd = 1; + break; + case 'c': + if (!strcmp(optarg, "mono")) channels = SOUND_CHANNELS_MONO; + else if (!strcmp(optarg, "left")) channels = SOUND_CHANNELS_LEFT; + else if (!strcmp(optarg, "right")) channels = SOUND_CHANNELS_RIGHT; + break; + case 'l': + show_levels = 1; + break; + case 'f': + file_name = optarg; + break; + case 'd': + debug_nmea = 1; + break; +#ifdef WIN32 + case 'D': + if (!strcmp(optarg, "list")) { + printInDevices(); + return EXIT_SUCCESS; + } else { + deviceId = atoi(optarg); + } + break; +#endif +#ifdef HAVE_ALSA + case 'D': + if (!strcmp(optarg, "list")) { + printInDevices(); + return EXIT_SUCCESS; + } else { + alsaDevice = optarg; + } + break; +#endif + case 'H': + default: + fprintf(stderr, HELP_MSG); + return EXIT_SUCCESS; + break; + } + } + + if (argc < 2) { + fprintf(stderr, HELP_MSG); + return EXIT_FAILURE; + } + + if (!hfnd) { + fprintf(stderr, "Host is not set\n"); + return EXIT_FAILURE; + } + + if (!pfnd) { + fprintf(stderr, "Port is not set\n"); + return EXIT_FAILURE; + } + + if (!afnd) { + fprintf(stderr, "Audio driver is not set\n"); + return EXIT_FAILURE; + } + + if (!alsa && !pulse && !winmm && !file) { + fprintf(stderr, "Invalid audio driver\n"); + return EXIT_FAILURE; + } + + if (!initSocket(host, port)) { + return EXIT_FAILURE; + } + if (show_levels) on_sound_level_changed=sound_level_changed; + on_nmea_sentence_received=nmea_sentence_received; + Sound_Driver driver = DRIVER_FILE; +#ifdef HAVE_ALSA + if (alsa) driver = DRIVER_ALSA; +#endif +#ifdef HAVE_PULSEAUDIO + if (pulse) driver = DRIVER_PULSE; +#endif + int OK=0; +#ifdef WIN32 + if (!file) driver = DRIVER_WINMM; + OK=initSoundDecoder(channels, driver, file_name, deviceId); +#else +#ifdef HAVE_ALSA + OK=initSoundDecoder(channels, driver, file_name, alsaDevice); +#else + OK=initSoundDecoder(channels, driver, file_name); +#endif +#endif + int stop=0; + if (OK) { + runSoundDecoder(&stop); + } else { + fprintf(stderr, "%s\n", errorSoundDecoder); + } + freeSoundDecoder(); + freeaddrinfo(addr); +#ifdef WIN32 + WSACleanup(); +#endif + return 0; +} + +int initSocket(const char *host, const char *portname) { + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family=AF_UNSPEC; + hints.ai_socktype=SOCK_DGRAM; + hints.ai_protocol=IPPROTO_UDP; +#ifndef WIN32 + hints.ai_flags=AI_ADDRCONFIG; +#else + + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed: %d\n", iResult); + return 0; + } +#endif + int err=getaddrinfo(host, portname, &hints, &addr); + if (err!=0) { + fprintf(stderr, "Failed to resolve remote socket address!\n"); +#ifdef WIN32 + WSACleanup(); +#endif + return 0; + } + + sock=socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (sock==-1) { + fprintf(stderr, "%s",strerror(errno)); +#ifdef WIN32 + WSACleanup(); +#endif + return 0; + } + return 1; +} + diff --git a/src/sounddecoder.c b/src/sounddecoder.c new file mode 100755 index 0000000..c2a6282 --- /dev/null +++ b/src/sounddecoder.c @@ -0,0 +1,231 @@ +/* + * sounddecoder.cpp + * + * This file is part of AISDecoder. + * + * Copyright (C) 2013 + * Astra Paging Ltd / AISHub (info@aishub.net) + * + * AISDecoder 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. + * + * AISDecoder uses parts of GNUAIS project (http://gnuais.sourceforge.net/) + * + */ + +#include +#include +#include "config.h" + +#include "receiver.h" +#include "hmalloc.h" +#ifdef HAVE_ALSA +#include "alsaaudio/alsaaudio.h" +#endif +#ifdef HAVE_PULSEAUDIO +#include "pulseaudio/pulseaudio.h" +#endif +#ifdef WIN32 +#include "winmmaudio/winmm_input.h" +#endif + +#define MAX_FILENAME_SIZE 512 +#define ERROR_MESSAGE_LENGTH 1024 +#include "sounddecoder.h" + + +char errorSoundDecoder[ERROR_MESSAGE_LENGTH]; + +static struct receiver *rx_a=NULL; +static struct receiver *rx_b=NULL; + +static short *buffer=NULL; +static int buffer_l=0; +static int buffer_read=0; +static int channels=0; +static Sound_Channels sound_channels; +static Sound_Driver driver; +static FILE *fp=NULL; + +static void readBuffers(); + +#ifdef HAVE_ALSA +static snd_pcm_t *pcm=NULL; +#endif +#ifdef HAVE_PULSEAUDIO +static pa_simple *pa_dev=NULL; +#endif +#ifdef WIN32 +static HWAVEIN winmm_device=0; +static HANDLE winmm_event=0; +#endif + +#ifndef WIN32 +#ifdef HAVE_ALSA +int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver,const char *file, const char *_alsaDevice) { +#else +int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file) { +#endif +#else +int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file, unsigned int deviceId) { +#endif + sound_channels = _channels; + driver = _driver; + channels = sound_channels == SOUND_CHANNELS_MONO ? 1 : 2; + errorSoundDecoder[0]=0; + char soundFile[MAX_FILENAME_SIZE+1]; + switch (driver) { + case DRIVER_FILE: + strncpy(soundFile, file, MAX_FILENAME_SIZE); + soundFile[MAX_FILENAME_SIZE]=0; + fp = fopen(soundFile, "rb"); + if (fp) { + buffer_l = 1024; + int extra = buffer_l % 5; + buffer_l -= extra; + buffer = (short *) hmalloc(buffer_l * sizeof(short) * channels); + } else { + strcpy(errorSoundDecoder, "Can't open raw file for read"); + return 0; + } + break; +#ifdef HAVE_PULSEAUDIO + case DRIVER_PULSE: + if((pa_dev = pulseaudio_initialize(channels, NULL)) == NULL){ + pa_dev=NULL; + strcpy(errorSoundDecoder, "Can't initialize pulse audio"); + return 0; + } else { + buffer_l = 1024; + int extra = buffer_l % 5; + buffer_l -= extra; + buffer = (short *) hmalloc(buffer_l * sizeof(short) * channels); + } + break; +#endif +#ifdef HAVE_ALSA + case DRIVER_ALSA: + if (snd_pcm_open(&pcm, (_alsaDevice != NULL ? _alsaDevice : "default"), SND_PCM_STREAM_CAPTURE, 0) < 0 ) { + strcpy(errorSoundDecoder, "Can't open default capture device"); + return 0; + } else { + if (input_initialize(pcm, &buffer, &buffer_l, channels, errorSoundDecoder) < 0) return 0; + } + break; +#endif +#ifdef WIN32 + case DRIVER_WINMM: + buffer_l = 4096; + int buffer_len_in_bytes = buffer_l * sizeof(short) * channels; + buffer = (short *) hmalloc(buffer_len_in_bytes); + if (!winmm_init(deviceId, &winmm_device, channels, buffer_len_in_bytes, &winmm_event)) { + strcpy(errorSoundDecoder, "Can't initialize windows audio"); + return 0; + } + break; +#endif + } + + if (sound_channels == SOUND_CHANNELS_MONO) { + rx_a = init_receiver('A', 1, 0); + } else { + rx_a = init_receiver('A', 2, 0); + rx_b = init_receiver('B', 2, 1); + } + return 1; +} + +void runSoundDecoder(int *stop) { +#ifdef WIN32 + waveInStart(winmm_device); +#endif + while (!*stop) { + switch (driver) { + case DRIVER_FILE: + buffer_read = fread(buffer, channels * sizeof(short), buffer_l, fp); + if (buffer_read <= 0) *stop = 1; + break; + +#ifdef HAVE_PULSEAUDIO + case DRIVER_PULSE: + buffer_read = pulseaudio_read(pa_dev, buffer, buffer_l, channels); + break; +#endif +#ifdef HAVE_ALSA + case DRIVER_ALSA: + buffer_read = alsa_read(pcm, buffer, buffer_l); + break; +#endif +#ifdef WIN32 + case DRIVER_WINMM: + buffer_read=0; + switch(WaitForSingleObject(winmm_event, 2000)) { + case WAIT_TIMEOUT: + buffer_read=0; + break; + case WAIT_OBJECT_0: + buffer_read = winmm_getRecorded(&winmm_device, (char*)buffer); + break; + } + break; + ResetEvent(winmm_event); +#endif + } + readBuffers(); + } +#ifdef WIN32 + waveInStop(winmm_device); +#endif +} + +static void readBuffers() { + if (buffer_read <= 0) return; + if (rx_a != NULL && sound_channels != SOUND_CHANNELS_RIGHT) + receiver_run(rx_a, buffer, buffer_read); + + if (rx_b != NULL && + (sound_channels == SOUND_CHANNELS_STEREO || sound_channels == SOUND_CHANNELS_RIGHT) + ) receiver_run(rx_b, buffer, buffer_read); +} + +void freeSoundDecoder() { +#ifdef HAVE_PULSEAUDIO + if (pa_dev) { + pulseaudio_cleanup(pa_dev); + pa_dev=NULL; + } +#endif + +#ifdef HAVE_ALSA + if (pcm != NULL) { + input_cleanup(pcm); + pcm = NULL; + } +#endif +#ifdef WIN32 + if (winmm_device) { + winmm_cleanup(&winmm_device, &winmm_event); + } +#endif + if (fp != NULL) { + fclose(fp); + fp=NULL; + } + + if (rx_a != NULL) { + free_receiver(rx_a); + rx_a=NULL; + } + + if (rx_b != NULL) { + free_receiver(rx_b); + rx_b=NULL; + } + + if (buffer != NULL) { + hfree(buffer); + buffer = NULL; + } +} diff --git a/src/sounddecoder.h b/src/sounddecoder.h new file mode 100755 index 0000000..18fe703 --- /dev/null +++ b/src/sounddecoder.h @@ -0,0 +1,45 @@ +#ifndef SOUNDDECODER_H +#define SOUNDDECODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SOUND_CHANNELS_MONO, + SOUND_CHANNELS_STEREO, + SOUND_CHANNELS_LEFT, + SOUND_CHANNELS_RIGHT +} Sound_Channels; + +typedef enum { +#ifdef HAVE_ALSA + DRIVER_ALSA, +#endif +#ifdef HAVE_PULSEAUDIO + DRIVER_PULSE, +#endif +#ifdef WIN32 + DRIVER_WINMM, +#endif + DRIVER_FILE +} Sound_Driver; + +extern char errorSoundDecoder[]; + +#ifndef WIN32 +#ifdef HAVE_ALSA +int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver,const char *file, const char *_alsaDevice); +#else +int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file); +#endif +#else +int initSoundDecoder(const Sound_Channels _channels, const Sound_Driver _driver, const char *file, unsigned int deviceId); +#endif +void runSoundDecoder(int *stop); +void freeSoundDecoder(); + +#ifdef __cplusplus +} +#endif +#endif // SOUNDDECODER_H